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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJaN8MiAAoJEG4XJFUm622bGN4H/jc7+JqGUMozK8CKe5UGFnu7
 HlwP3Vpz7SR655CgoMzvNzJ6lvBxaPA77epPFkALuwua3J22feakv5UGipT7RPI/
 EtFCtq6+dIB+qooJ/8hUQVfAV8o13+dQzBQqtp7Wg37ok0qhcGpTLsvf2rI0ZG1R
 +lcC2Jyk0lYjAPuPri3+KjxPLkZhGbx/hCdKwxQfCoubEVoqimMcQ68+RqU3rxNB
 Of2Sk8IsaIevantLPnmO0+9OhZiMyoy4QGSnnuHntdpgZqEl0NbmVshQONCU9oTu
 3RPKvbbYe57gRfgLKEvqTvij5R8ZxxwF+BFacaXch7Q9k6pMoJuyD6gJ8/S8AW8=
 =FTCb
 -----END PGP SIGNATURE-----

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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-12-19 14:04:52 -05:00
commit b8fa3bfb14
159 changed files with 12713 additions and 2110 deletions

View file

@ -0,0 +1,32 @@
* 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>;
};
};
};
};

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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.bmi_ids_valid) {
scnprintf(name, name_len,
"bus=%s,bmi-chip-id=%d,bmi-board-id=%d",
ath10k_bus_str(ar->hif.bus),
ar->id.bmi_chip_id,
ar->id.bmi_board_id);
goto out;
}
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%s",
ath10k_bus_str(ar->hif.bus),
ar->id.bmi_chip_id,
ar->id.bmi_board_id, variant);
goto out;
}
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) {

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -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 = {};

View file

@ -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

View file

@ -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);

View file

@ -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 */

View file

@ -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;

View file

@ -917,33 +917,69 @@ ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len,
return -ENOMEM;
}
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;
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 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,

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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.

View file

@ -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 \

View file

@ -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 */

View file

@ -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);

View file

@ -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;

View file

@ -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 {

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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 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,
"rejected by device:%ld\n"
"Radio off suspend statistics:\n"
"successful suspends:%ld failed suspends:%ld\n"
"successful resumes:%ld failed resumes:%ld\n"
"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, sizeof(text));
n = min_t(int, n, text_size);
return simple_read_from_buffer(user_buf, count, ppos, text, n);
ret = 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
*/

View file

@ -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:

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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 = {

View file

@ -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 = {

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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 */

View file

@ -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,69 @@ 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;
if (bar0 == sdiodev->sbwad)
return 0;
addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
v = bar0 >> 8;
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;
}
}
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;
return err;
}
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;
if (bar0 != sdiodev->sbwad) {
err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
if (err)
return err;
sdiodev->sbwad = bar0;
}
*addr &= SBSDIO_SB_OFT_ADDR_MASK;
if (width == 4)
*addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
return 0;
}
u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
{
u8 data;
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);
if (ret)
*ret = retval;
return data;
}
u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
u32 brcmf_sdiod_readl(struct brcmf_sdio_dev *sdiodev, u32 addr, 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);
goto out;
done:
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;
}
void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
u8 data, int *ret)
void brcmf_sdiod_writel(struct brcmf_sdio_dev *sdiodev, u32 addr,
u32 data, int *ret)
{
int retval;
brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
true);
if (ret)
*ret = retval;
}
void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
u32 data, int *ret)
{
int retval;
brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
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,
true);
goto out;
done:
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
sdio_writel(sdiodev->func[1], data, addr, &retval);
out:
if (ret)
*ret = retval;
}
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_read(struct brcmf_sdio_dev *sdiodev, uint fn,
u32 addr, struct sk_buff *pkt)
{
unsigned int req_sz;
int err;
@ -476,18 +301,36 @@ 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);
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_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_buff_write(struct brcmf_sdio_dev *sdiodev, uint fn,
u32 addr, struct sk_buff *pkt)
{
unsigned int req_sz;
int err;
/* Single skb use the standard mmc interface */
req_sz = pkt->len + 3;
req_sz &= (uint)~3;
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;

View file

@ -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++) {

View file

@ -159,8 +159,8 @@ struct rte_console {
/* manfid tuple length, include tuple, link bytes */
#define SBSDIO_CIS_MANFID_TUPLE_LEN 6
#define CORE_BUS_REG(base, field) \
(base + offsetof(struct sdpcmd_regs, field))
#define SD_REG(field) \
(offsetof(struct sdpcmd_regs, field))
/* SDIO function 1 register CHIPCLKCSR */
/* Force ALP request to backplane */
@ -436,6 +436,7 @@ struct brcmf_sdio_count {
struct brcmf_sdio {
struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
struct brcmf_chip *ci; /* Chip info struct */
struct brcmf_core *sdio_core; /* sdio core info struct */
u32 hostintmask; /* Copy of Host Interrupt Mask */
atomic_t intstatus; /* Intstatus bits (events) pending */
@ -665,22 +666,20 @@ static bool data_ok(struct brcmf_sdio *bus)
*/
static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
{
struct brcmf_core *core;
struct brcmf_core *core = bus->sdio_core;
int ret;
core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
*regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
*regvar = brcmf_sdiod_readl(bus->sdiodev, core->base + offset, &ret);
return ret;
}
static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
{
struct brcmf_core *core;
struct brcmf_core *core = bus->sdio_core;
int ret;
core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
brcmf_sdiod_writel(bus->sdiodev, core->base + reg_offset, regval, &ret);
return ret;
}
@ -697,8 +696,7 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
/* 1st KSO write goes to AOS wake up core if device is asleep */
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
wr_val, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
if (on) {
/* device WAKEUP through KSO:
@ -724,7 +722,7 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
* just one write attempt may fail,
* read it back until it matches written value
*/
rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
rd_val = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
&err);
if (!err) {
if ((rd_val & bmask) == cmp_val)
@ -734,9 +732,11 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
/* bail out upon subsequent access errors */
if (err && (err_cnt++ > BRCMF_SDIO_MAX_ACCESS_ERRORS))
break;
udelay(KSO_WAIT_US);
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
wr_val, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val,
&err);
} while (try_cnt++ < MAX_KSO_ATTEMPTS);
if (try_cnt > 2)
@ -772,15 +772,15 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
clkreq =
bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
clkreq, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
clkreq, &err);
if (err) {
brcmf_err("HT Avail request error: %d\n", err);
return -EBADE;
}
/* Check current status */
clkctl = brcmf_sdiod_regrb(bus->sdiodev,
clkctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR, &err);
if (err) {
brcmf_err("HT Avail read error: %d\n", err);
@ -790,35 +790,34 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
/* Go to pending and await interrupt if appropriate */
if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
/* Allow only clock-available interrupt */
devctl = brcmf_sdiod_regrb(bus->sdiodev,
devctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_DEVICE_CTL, &err);
if (err) {
brcmf_err("Devctl error setting CA: %d\n",
err);
brcmf_err("Devctl error setting CA: %d\n", err);
return -EBADE;
}
devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
bus->clkstate = CLK_PENDING;
return 0;
} else if (bus->clkstate == CLK_PENDING) {
/* Cancel CA-only interrupt filter */
devctl = brcmf_sdiod_regrb(bus->sdiodev,
devctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_DEVICE_CTL, &err);
devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
}
/* Otherwise, wait here (polling) for HT Avail */
timeout = jiffies +
msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
clkctl = brcmf_sdiod_regrb(bus->sdiodev,
clkctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
&err);
if (time_after(jiffies, timeout))
@ -852,16 +851,16 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
if (bus->clkstate == CLK_PENDING) {
/* Cancel CA-only interrupt filter */
devctl = brcmf_sdiod_regrb(bus->sdiodev,
devctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_DEVICE_CTL, &err);
devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
}
bus->clkstate = CLK_SDONLY;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
clkreq, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
clkreq, &err);
brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
if (err) {
brcmf_err("Failed access turning clock off: %d\n",
@ -951,14 +950,14 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
/* Going to sleep */
if (sleep) {
clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
clkcsr = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
&err);
if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
brcmf_dbg(SDIO, "no clock, set ALP\n");
brcmf_sdiod_regwb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_ALP_AVAIL_REQ, &err);
brcmf_sdiod_writeb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_ALP_AVAIL_REQ, &err);
}
err = brcmf_sdio_kso_control(bus, false);
} else {
@ -1087,12 +1086,10 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
brcmf_dbg(SDIO, "Enter\n");
/* Read mailbox data and ack that we did so */
ret = r_sdreg32(bus, &hmb_data,
offsetof(struct sdpcmd_regs, tohostmailboxdata));
ret = r_sdreg32(bus, &hmb_data, SD_REG(tohostmailboxdata));
if (ret == 0)
w_sdreg32(bus, SMB_INT_ACK,
offsetof(struct sdpcmd_regs, tosbmailbox));
w_sdreg32(bus, SMB_INT_ACK, SD_REG(tosbmailbox));
bus->sdcnt.f1regdata += 2;
/* dongle indicates the firmware has halted/crashed */
@ -1178,16 +1175,16 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
if (abort)
brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
SFC_RF_TERM, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM,
&err);
bus->sdcnt.f1regdata++;
/* Wait until the packet has been flushed (device/FIFO stable) */
for (lastrbc = retries = 0xffff; retries > 0; retries--) {
hi = brcmf_sdiod_regrb(bus->sdiodev,
SBSDIO_FUNC1_RFRAMEBCHI, &err);
lo = brcmf_sdiod_regrb(bus->sdiodev,
SBSDIO_FUNC1_RFRAMEBCLO, &err);
hi = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCHI,
&err);
lo = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCLO,
&err);
bus->sdcnt.f1regdata += 2;
if ((hi == 0) && (lo == 0))
@ -1207,8 +1204,7 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
if (rtx) {
bus->sdcnt.rxrtx++;
err = w_sdreg32(bus, SMB_NAK,
offsetof(struct sdpcmd_regs, tosbmailbox));
err = w_sdreg32(bus, SMB_NAK, SD_REG(tosbmailbox));
bus->sdcnt.f1regdata++;
if (err == 0)
@ -1229,12 +1225,12 @@ static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
bus->sdcnt.tx_sderrs++;
brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2);
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
bus->sdcnt.f1regdata++;
for (i = 0; i < 3; i++) {
hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
hi = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
lo = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
bus->sdcnt.f1regdata += 2;
if ((hi == 0) && (lo == 0))
break;
@ -2333,9 +2329,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
if (!bus->intr) {
/* Check device status, signal pending interrupt */
sdio_claim_host(bus->sdiodev->func[1]);
ret = r_sdreg32(bus, &intstatus,
offsetof(struct sdpcmd_regs,
intstatus));
ret = r_sdreg32(bus, &intstatus, SD_REG(intstatus));
sdio_release_host(bus->sdiodev->func[1]);
bus->sdcnt.f2txdata++;
if (ret != 0)
@ -2441,16 +2435,16 @@ static void brcmf_sdio_bus_stop(struct device *dev)
brcmf_sdio_bus_sleep(bus, false, false);
/* Disable and clear interrupts at the chip level also */
w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
w_sdreg32(bus, 0, SD_REG(hostintmask));
local_hostintmask = bus->hostintmask;
bus->hostintmask = 0;
/* Force backplane clocks to assure F2 interrupt propagates */
saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
saveclk = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
&err);
if (!err)
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
(saveclk | SBSDIO_FORCE_HT), &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
(saveclk | SBSDIO_FORCE_HT), &err);
if (err)
brcmf_err("Failed to force clock for F2: err %d\n",
err);
@ -2460,8 +2454,7 @@ static void brcmf_sdio_bus_stop(struct device *dev)
sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
/* Clear any pending interrupts now that F2 is disabled */
w_sdreg32(bus, local_hostintmask,
offsetof(struct sdpcmd_regs, intstatus));
w_sdreg32(bus, local_hostintmask, SD_REG(intstatus));
sdio_release_host(sdiodev->func[1]);
}
@ -2501,15 +2494,14 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
{
struct brcmf_core *buscore;
struct brcmf_core *buscore = bus->sdio_core;
u32 addr;
unsigned long val;
int ret;
buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
addr = buscore->base + SD_REG(intstatus);
val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
val = brcmf_sdiod_readl(bus->sdiodev, addr, &ret);
bus->sdcnt.f1regdata++;
if (ret != 0)
return ret;
@ -2519,7 +2511,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
/* Clear interrupts */
if (val) {
brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
brcmf_sdiod_writel(bus->sdiodev, addr, val, &ret);
bus->sdcnt.f1regdata++;
atomic_or(val, &bus->intstatus);
}
@ -2545,23 +2537,23 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
#ifdef DEBUG
/* Check for inconsistent device control */
devctl = brcmf_sdiod_regrb(bus->sdiodev,
SBSDIO_DEVICE_CTL, &err);
devctl = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_DEVICE_CTL,
&err);
#endif /* DEBUG */
/* Read CSR, if clock on switch to AVAIL, else ignore */
clkctl = brcmf_sdiod_regrb(bus->sdiodev,
clkctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR, &err);
brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
devctl, clkctl);
if (SBSDIO_HTAV(clkctl)) {
devctl = brcmf_sdiod_regrb(bus->sdiodev,
devctl = brcmf_sdiod_readb(bus->sdiodev,
SBSDIO_DEVICE_CTL, &err);
devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
devctl, &err);
brcmf_sdiod_writeb(bus->sdiodev,
SBSDIO_DEVICE_CTL, devctl, &err);
bus->clkstate = CLK_AVAIL;
}
}
@ -2584,11 +2576,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
*/
if (intstatus & I_HMB_FC_CHANGE) {
intstatus &= ~I_HMB_FC_CHANGE;
err = w_sdreg32(bus, I_HMB_FC_CHANGE,
offsetof(struct sdpcmd_regs, intstatus));
err = w_sdreg32(bus, I_HMB_FC_CHANGE, SD_REG(intstatus));
err = r_sdreg32(bus, &newstatus,
offsetof(struct sdpcmd_regs, intstatus));
err = r_sdreg32(bus, &newstatus, SD_REG(intstatus));
bus->sdcnt.f1regdata += 2;
atomic_set(&bus->fcstate,
!!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
@ -3347,31 +3337,31 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
brcmf_dbg(TRACE, "Enter\n");
val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
val = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
if (err) {
brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
return;
}
val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
if (err) {
brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
return;
}
/* Add CMD14 Support */
brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
(SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
&err);
brcmf_sdiod_func0_wb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
(SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
&err);
if (err) {
brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
return;
}
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_FORCE_HT, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_FORCE_HT, &err);
if (err) {
brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
return;
@ -3385,16 +3375,17 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
/* enable KSO bit */
static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
{
struct brcmf_core *core = bus->sdio_core;
u8 val;
int err = 0;
brcmf_dbg(TRACE, "Enter\n");
/* KSO bit added in SDIO core rev 12 */
if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
if (core->rev < 12)
return 0;
val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
val = brcmf_sdiod_readb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
if (err) {
brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
return err;
@ -3403,8 +3394,8 @@ static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
val, &err);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
val, &err);
if (err) {
brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
return err;
@ -3420,6 +3411,7 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
struct brcmf_core *core = bus->sdio_core;
uint pad_size;
u32 value;
int err;
@ -3428,7 +3420,7 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
* a device perspective, ie. bus:txglom affects the
* bus transfers from device to host.
*/
if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
if (core->rev < 12) {
/* for sdio core rev < 12, disable txgloming */
value = 0;
err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
@ -3565,9 +3557,9 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
u8 devpend;
sdio_claim_host(bus->sdiodev->func[1]);
devpend = brcmf_sdiod_regrb(bus->sdiodev,
SDIO_CCCR_INTx,
NULL);
devpend = brcmf_sdiod_func0_rb(bus->sdiodev,
SDIO_CCCR_INTx,
NULL);
sdio_release_host(bus->sdiodev->func[1]);
intstatus = devpend & (INTR_STATUS_FUNC1 |
INTR_STATUS_FUNC2);
@ -3705,12 +3697,12 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
}
}
addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
brcmf_sdiod_writel(sdiodev, addr, 1, NULL);
cc_data_temp = brcmf_sdiod_readl(sdiodev, addr, NULL);
cc_data_temp &= ~str_mask;
drivestrength_sel <<= str_shift;
cc_data_temp |= drivestrength_sel;
brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
brcmf_sdiod_writel(sdiodev, addr, cc_data_temp, NULL);
brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
str_tab[i].strength, drivestrength, cc_data_temp);
@ -3725,7 +3717,7 @@ static int brcmf_sdio_buscoreprep(void *ctx)
/* Try forcing SDIO core to do ALPAvail request only */
clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
if (err) {
brcmf_err("error writing for HT off\n");
return err;
@ -3733,8 +3725,7 @@ static int brcmf_sdio_buscoreprep(void *ctx)
/* If register supported, wait for ALPAvail and then force ALP */
/* This may take up to 15 milliseconds */
clkval = brcmf_sdiod_regrb(sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR, NULL);
clkval = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, NULL);
if ((clkval & ~SBSDIO_AVBITS) != clkset) {
brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
@ -3742,10 +3733,11 @@ static int brcmf_sdio_buscoreprep(void *ctx)
return -EACCES;
}
SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
!SBSDIO_ALPAV(clkval)),
PMU_MAX_TRANSITION_DLY);
SPINWAIT(((clkval = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
NULL)),
!SBSDIO_ALPAV(clkval)),
PMU_MAX_TRANSITION_DLY);
if (!SBSDIO_ALPAV(clkval)) {
brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
clkval);
@ -3753,11 +3745,11 @@ static int brcmf_sdio_buscoreprep(void *ctx)
}
clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
udelay(65);
/* Also, disable the extra SDIO pull-ups */
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
return 0;
}
@ -3766,13 +3758,12 @@ static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip,
u32 rstvec)
{
struct brcmf_sdio_dev *sdiodev = ctx;
struct brcmf_core *core;
struct brcmf_core *core = sdiodev->bus->sdio_core;
u32 reg_addr;
/* clear all interrupts */
core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
reg_addr = core->base + SD_REG(intstatus);
brcmf_sdiod_writel(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
if (rstvec)
/* Write reset vector to address 0 */
@ -3785,7 +3776,7 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
struct brcmf_sdio_dev *sdiodev = ctx;
u32 val, rev;
val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
val = brcmf_sdiod_readl(sdiodev, addr, NULL);
if ((sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 ||
sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4339) &&
addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
@ -3802,7 +3793,7 @@ static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
{
struct brcmf_sdio_dev *sdiodev = ctx;
brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
brcmf_sdiod_writel(sdiodev, addr, val, NULL);
}
static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
@ -3826,18 +3817,18 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
sdio_claim_host(sdiodev->func[1]);
pr_debug("F1 signature read @0x18000000=0x%4x\n",
brcmf_sdiod_regrl(sdiodev, SI_ENUM_BASE, NULL));
brcmf_sdiod_readl(sdiodev, SI_ENUM_BASE, NULL));
/*
* Force PLL off until brcmf_chip_attach()
* programs PLL control regs
*/
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
BRCMF_INIT_CLKCTL1, &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, BRCMF_INIT_CLKCTL1,
&err);
if (!err)
clkctl = brcmf_sdiod_regrb(sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR, &err);
clkctl = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
&err);
if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
@ -3851,6 +3842,12 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
bus->ci = NULL;
goto fail;
}
/* Pick up the SDIO core info struct from chip.c */
bus->sdio_core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
if (!bus->sdio_core)
goto fail;
sdiodev->settings = brcmf_get_module_param(sdiodev->dev,
BRCMF_BUSTYPE_SDIO,
bus->ci->chip,
@ -3897,25 +3894,25 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
brcmf_sdio_drivestrengthinit(sdiodev, bus->ci, drivestrength);
/* Set card control so an SDIO card reset does a WLAN backplane reset */
reg_val = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err);
reg_val = brcmf_sdiod_func0_rb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err);
if (err)
goto fail;
reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
if (err)
goto fail;
/* set PMUControl so a backplane reset does PMU state reload */
reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
reg_val = brcmf_sdiod_regrl(sdiodev, reg_addr, &err);
reg_val = brcmf_sdiod_readl(sdiodev, reg_addr, &err);
if (err)
goto fail;
reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
brcmf_sdiod_regwl(sdiodev, reg_addr, reg_val, &err);
brcmf_sdiod_writel(sdiodev, reg_addr, reg_val, &err);
if (err)
goto fail;
@ -4055,10 +4052,10 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
goto release;
/* Force clocks on backplane to be sure F2 interrupt propagates */
saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
saveclk = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
if (!err) {
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
(saveclk | SBSDIO_FORCE_HT), &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
(saveclk | SBSDIO_FORCE_HT), &err);
}
if (err) {
brcmf_err("Failed to force clock for F2: err %d\n", err);
@ -4067,7 +4064,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
/* Enable function 2 (frame transfers) */
w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
offsetof(struct sdpcmd_regs, tosbmailboxdata));
SD_REG(tosbmailboxdata));
err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
@ -4077,10 +4074,9 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
if (!err) {
/* Set up the interrupt mask and enable interrupts */
bus->hostintmask = HOSTINTMASK;
w_sdreg32(bus, bus->hostintmask,
offsetof(struct sdpcmd_regs, hostintmask));
w_sdreg32(bus, bus->hostintmask, SD_REG(hostintmask));
brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_WATERMARK, 8, &err);
} else {
/* Disable F2 again */
sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
@ -4091,8 +4087,8 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
brcmf_sdio_sr_init(bus);
} else {
/* Restore previous clock setting */
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
saveclk, &err);
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
saveclk, &err);
}
if (err == 0) {
@ -4224,7 +4220,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
bus->rxflow = false;
/* Done with backplane-dependent accesses, can drop clock... */
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
sdio_release_host(bus->sdiodev->func[1]);

View file

@ -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_SEPINT_MASK 0x01
#define SDIO_SEPINT_OE 0x02
#define SDIO_SEPINT_ACT_HI 0x04
#define SDIO_CCCR_BRCM_CARDCAP 0xf0
#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_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);

View file

@ -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));

View file

@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <types.h>
#include "phytbl_n.h"
@ -4437,109 +4438,39 @@ static const u16 loft_lut_core1_rev0[] = {
};
const struct phytbl_info mimophytbl_info_rev0_volatile[] = {
{&bdi_tbl_rev0, sizeof(bdi_tbl_rev0) / sizeof(bdi_tbl_rev0[0]), 21, 0,
16}
,
{&pltlut_tbl_rev0, sizeof(pltlut_tbl_rev0) / sizeof(pltlut_tbl_rev0[0]),
20, 0, 32}
,
{&gainctrl_lut_core0_rev0,
sizeof(gainctrl_lut_core0_rev0) / sizeof(gainctrl_lut_core0_rev0[0]),
26, 192, 32}
,
{&gainctrl_lut_core1_rev0,
sizeof(gainctrl_lut_core1_rev0) / sizeof(gainctrl_lut_core1_rev0[0]),
27, 192, 32}
,
{&est_pwr_lut_core0_rev0,
sizeof(est_pwr_lut_core0_rev0) / sizeof(est_pwr_lut_core0_rev0[0]), 26,
0, 8}
,
{&est_pwr_lut_core1_rev0,
sizeof(est_pwr_lut_core1_rev0) / sizeof(est_pwr_lut_core1_rev0[0]), 27,
0, 8}
,
{&adj_pwr_lut_core0_rev0,
sizeof(adj_pwr_lut_core0_rev0) / sizeof(adj_pwr_lut_core0_rev0[0]), 26,
64, 8}
,
{&adj_pwr_lut_core1_rev0,
sizeof(adj_pwr_lut_core1_rev0) / sizeof(adj_pwr_lut_core1_rev0[0]), 27,
64, 8}
,
{&iq_lut_core0_rev0,
sizeof(iq_lut_core0_rev0) / sizeof(iq_lut_core0_rev0[0]), 26, 320, 32}
,
{&iq_lut_core1_rev0,
sizeof(iq_lut_core1_rev0) / sizeof(iq_lut_core1_rev0[0]), 27, 320, 32}
,
{&loft_lut_core0_rev0,
sizeof(loft_lut_core0_rev0) / sizeof(loft_lut_core0_rev0[0]), 26, 448,
16}
,
{&loft_lut_core1_rev0,
sizeof(loft_lut_core1_rev0) / sizeof(loft_lut_core1_rev0[0]), 27, 448,
16}
,
{&bdi_tbl_rev0, ARRAY_SIZE(bdi_tbl_rev0), 21, 0, 16},
{&pltlut_tbl_rev0, ARRAY_SIZE(pltlut_tbl_rev0), 20, 0, 32},
{&gainctrl_lut_core0_rev0, ARRAY_SIZE(gainctrl_lut_core0_rev0), 26, 192, 32},
{&gainctrl_lut_core1_rev0, ARRAY_SIZE(gainctrl_lut_core1_rev0), 27, 192, 32},
{&est_pwr_lut_core0_rev0, ARRAY_SIZE(est_pwr_lut_core0_rev0), 26, 0, 8},
{&est_pwr_lut_core1_rev0, ARRAY_SIZE(est_pwr_lut_core1_rev0), 27, 0, 8},
{&adj_pwr_lut_core0_rev0, ARRAY_SIZE(adj_pwr_lut_core0_rev0), 26, 64, 8},
{&adj_pwr_lut_core1_rev0, ARRAY_SIZE(adj_pwr_lut_core1_rev0), 27, 64, 8},
{&iq_lut_core0_rev0, ARRAY_SIZE(iq_lut_core0_rev0), 26, 320, 32},
{&iq_lut_core1_rev0, ARRAY_SIZE(iq_lut_core1_rev0), 27, 320, 32},
{&loft_lut_core0_rev0, ARRAY_SIZE(loft_lut_core0_rev0), 26, 448, 16},
{&loft_lut_core1_rev0, ARRAY_SIZE(loft_lut_core1_rev0), 27, 448, 16},
};
const struct phytbl_info mimophytbl_info_rev0[] = {
{&frame_struct_rev0,
sizeof(frame_struct_rev0) / sizeof(frame_struct_rev0[0]), 10, 0, 32}
,
{&frame_lut_rev0, sizeof(frame_lut_rev0) / sizeof(frame_lut_rev0[0]),
24, 0, 8}
,
{&tmap_tbl_rev0, sizeof(tmap_tbl_rev0) / sizeof(tmap_tbl_rev0[0]), 12,
0, 32}
,
{&tdtrn_tbl_rev0, sizeof(tdtrn_tbl_rev0) / sizeof(tdtrn_tbl_rev0[0]),
14, 0, 32}
,
{&intlv_tbl_rev0, sizeof(intlv_tbl_rev0) / sizeof(intlv_tbl_rev0[0]),
13, 0, 32}
,
{&pilot_tbl_rev0, sizeof(pilot_tbl_rev0) / sizeof(pilot_tbl_rev0[0]),
11, 0, 16}
,
{&tdi_tbl20_ant0_rev0,
sizeof(tdi_tbl20_ant0_rev0) / sizeof(tdi_tbl20_ant0_rev0[0]), 19, 128,
32}
,
{&tdi_tbl20_ant1_rev0,
sizeof(tdi_tbl20_ant1_rev0) / sizeof(tdi_tbl20_ant1_rev0[0]), 19, 256,
32}
,
{&tdi_tbl40_ant0_rev0,
sizeof(tdi_tbl40_ant0_rev0) / sizeof(tdi_tbl40_ant0_rev0[0]), 19, 640,
32}
,
{&tdi_tbl40_ant1_rev0,
sizeof(tdi_tbl40_ant1_rev0) / sizeof(tdi_tbl40_ant1_rev0[0]), 19, 768,
32}
,
{&chanest_tbl_rev0,
sizeof(chanest_tbl_rev0) / sizeof(chanest_tbl_rev0[0]), 22, 0, 32}
,
{&mcs_tbl_rev0, sizeof(mcs_tbl_rev0) / sizeof(mcs_tbl_rev0[0]), 18, 0,
8}
,
{&noise_var_tbl0_rev0,
sizeof(noise_var_tbl0_rev0) / sizeof(noise_var_tbl0_rev0[0]), 16, 0,
32}
,
{&noise_var_tbl1_rev0,
sizeof(noise_var_tbl1_rev0) / sizeof(noise_var_tbl1_rev0[0]), 16, 128,
32}
,
{&frame_struct_rev0, ARRAY_SIZE(frame_struct_rev0), 10, 0, 32},
{&frame_lut_rev0, ARRAY_SIZE(frame_lut_rev0), 24, 0, 8},
{&tmap_tbl_rev0, ARRAY_SIZE(tmap_tbl_rev0), 12, 0, 32},
{&tdtrn_tbl_rev0, ARRAY_SIZE(tdtrn_tbl_rev0), 14, 0, 32},
{&intlv_tbl_rev0, ARRAY_SIZE(intlv_tbl_rev0), 13, 0, 32},
{&pilot_tbl_rev0, ARRAY_SIZE(pilot_tbl_rev0), 11, 0, 16},
{&tdi_tbl20_ant0_rev0, ARRAY_SIZE(tdi_tbl20_ant0_rev0), 19, 128, 32},
{&tdi_tbl20_ant1_rev0, ARRAY_SIZE(tdi_tbl20_ant1_rev0), 19, 256, 32},
{&tdi_tbl40_ant0_rev0, ARRAY_SIZE(tdi_tbl40_ant0_rev0), 19, 640, 32},
{&tdi_tbl40_ant1_rev0, ARRAY_SIZE(tdi_tbl40_ant1_rev0), 19, 768, 32},
{&chanest_tbl_rev0, ARRAY_SIZE(chanest_tbl_rev0), 22, 0, 32},
{&mcs_tbl_rev0, ARRAY_SIZE(mcs_tbl_rev0), 18, 0, 8},
{&noise_var_tbl0_rev0, ARRAY_SIZE(noise_var_tbl0_rev0), 16, 0, 32},
{&noise_var_tbl1_rev0, ARRAY_SIZE(noise_var_tbl1_rev0), 16, 128, 32},
};
const u32 mimophytbl_info_sz_rev0 =
sizeof(mimophytbl_info_rev0) / sizeof(mimophytbl_info_rev0[0]);
const u32 mimophytbl_info_sz_rev0_volatile =
sizeof(mimophytbl_info_rev0_volatile) /
sizeof(mimophytbl_info_rev0_volatile[0]);
const u32 mimophytbl_info_sz_rev0 = ARRAY_SIZE(mimophytbl_info_rev0);
const u32 mimophytbl_info_sz_rev0_volatile = ARRAY_SIZE(mimophytbl_info_rev0_volatile);
static const u16 ant_swctrl_tbl_rev3[] = {
0x0082,
@ -9363,132 +9294,53 @@ static const u32 papd_cal_scalars_tbl_core1_rev3[] = {
};
const struct phytbl_info mimophytbl_info_rev3_volatile[] = {
{&ant_swctrl_tbl_rev3,
sizeof(ant_swctrl_tbl_rev3) / sizeof(ant_swctrl_tbl_rev3[0]), 9, 0, 16}
,
{&ant_swctrl_tbl_rev3, ARRAY_SIZE(ant_swctrl_tbl_rev3), 9, 0, 16},
};
const struct phytbl_info mimophytbl_info_rev3_volatile1[] = {
{&ant_swctrl_tbl_rev3_1,
sizeof(ant_swctrl_tbl_rev3_1) / sizeof(ant_swctrl_tbl_rev3_1[0]), 9, 0,
16}
,
{&ant_swctrl_tbl_rev3_1, ARRAY_SIZE(ant_swctrl_tbl_rev3_1), 9, 0, 16},
};
const struct phytbl_info mimophytbl_info_rev3_volatile2[] = {
{&ant_swctrl_tbl_rev3_2,
sizeof(ant_swctrl_tbl_rev3_2) / sizeof(ant_swctrl_tbl_rev3_2[0]), 9, 0,
16}
,
{&ant_swctrl_tbl_rev3_2, ARRAY_SIZE(ant_swctrl_tbl_rev3_2), 9, 0, 16},
};
const struct phytbl_info mimophytbl_info_rev3_volatile3[] = {
{&ant_swctrl_tbl_rev3_3,
sizeof(ant_swctrl_tbl_rev3_3) / sizeof(ant_swctrl_tbl_rev3_3[0]), 9, 0,
16}
,
{&ant_swctrl_tbl_rev3_3, ARRAY_SIZE(ant_swctrl_tbl_rev3_3), 9, 0, 16},
};
const struct phytbl_info mimophytbl_info_rev3[] = {
{&frame_struct_rev3,
sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32}
,
{&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]),
11, 0, 16}
,
{&tmap_tbl_rev3, sizeof(tmap_tbl_rev3) / sizeof(tmap_tbl_rev3[0]), 12,
0, 32}
,
{&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]),
13, 0, 32}
,
{&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]),
14, 0, 32}
,
{&noise_var_tbl_rev3,
sizeof(noise_var_tbl_rev3) / sizeof(noise_var_tbl_rev3[0]), 16, 0, 32}
,
{&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0,
16}
,
{&tdi_tbl20_ant0_rev3,
sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128,
32}
,
{&tdi_tbl20_ant1_rev3,
sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256,
32}
,
{&tdi_tbl40_ant0_rev3,
sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640,
32}
,
{&tdi_tbl40_ant1_rev3,
sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768,
32}
,
{&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]),
20, 0, 32}
,
{&chanest_tbl_rev3,
sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32}
,
{&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]),
24, 0, 8}
,
{&est_pwr_lut_core0_rev3,
sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26,
0, 8}
,
{&est_pwr_lut_core1_rev3,
sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27,
0, 8}
,
{&adj_pwr_lut_core0_rev3,
sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26,
64, 8}
,
{&adj_pwr_lut_core1_rev3,
sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27,
64, 8}
,
{&gainctrl_lut_core0_rev3,
sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]),
26, 192, 32}
,
{&gainctrl_lut_core1_rev3,
sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]),
27, 192, 32}
,
{&iq_lut_core0_rev3,
sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32}
,
{&iq_lut_core1_rev3,
sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32}
,
{&loft_lut_core0_rev3,
sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448,
16}
,
{&loft_lut_core1_rev3,
sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448,
16}
{&frame_struct_rev3, ARRAY_SIZE(frame_struct_rev3), 10, 0, 32},
{&pilot_tbl_rev3, ARRAY_SIZE(pilot_tbl_rev3), 11, 0, 16},
{&tmap_tbl_rev3, ARRAY_SIZE(tmap_tbl_rev3), 12, 0, 32},
{&intlv_tbl_rev3, ARRAY_SIZE(intlv_tbl_rev3), 13, 0, 32},
{&tdtrn_tbl_rev3, ARRAY_SIZE(tdtrn_tbl_rev3), 14, 0, 32},
{&noise_var_tbl_rev3, ARRAY_SIZE(noise_var_tbl_rev3), 16, 0, 32},
{&mcs_tbl_rev3, ARRAY_SIZE(mcs_tbl_rev3), 18, 0, 16},
{&tdi_tbl20_ant0_rev3, ARRAY_SIZE(tdi_tbl20_ant0_rev3), 19, 128, 32},
{&tdi_tbl20_ant1_rev3, ARRAY_SIZE(tdi_tbl20_ant1_rev3), 19, 256, 32},
{&tdi_tbl40_ant0_rev3, ARRAY_SIZE(tdi_tbl40_ant0_rev3), 19, 640, 32},
{&tdi_tbl40_ant1_rev3, ARRAY_SIZE(tdi_tbl40_ant1_rev3), 19, 768, 32},
{&pltlut_tbl_rev3, ARRAY_SIZE(pltlut_tbl_rev3), 20, 0, 32},
{&chanest_tbl_rev3, ARRAY_SIZE(chanest_tbl_rev3), 22, 0, 32},
{&frame_lut_rev3, ARRAY_SIZE(frame_lut_rev3), 24, 0, 8},
{&est_pwr_lut_core0_rev3, ARRAY_SIZE(est_pwr_lut_core0_rev3), 26, 0, 8},
{&est_pwr_lut_core1_rev3, ARRAY_SIZE(est_pwr_lut_core1_rev3), 27, 0, 8},
{&adj_pwr_lut_core0_rev3, ARRAY_SIZE(adj_pwr_lut_core0_rev3), 26, 64, 8},
{&adj_pwr_lut_core1_rev3, ARRAY_SIZE(adj_pwr_lut_core1_rev3), 27, 64, 8},
{&gainctrl_lut_core0_rev3, ARRAY_SIZE(gainctrl_lut_core0_rev3), 26, 192, 32},
{&gainctrl_lut_core1_rev3, ARRAY_SIZE(gainctrl_lut_core1_rev3), 27, 192, 32},
{&iq_lut_core0_rev3, ARRAY_SIZE(iq_lut_core0_rev3), 26, 320, 32},
{&iq_lut_core1_rev3, ARRAY_SIZE(iq_lut_core1_rev3), 27, 320, 32},
{&loft_lut_core0_rev3, ARRAY_SIZE(loft_lut_core0_rev3), 26, 448, 16},
{&loft_lut_core1_rev3, ARRAY_SIZE(loft_lut_core1_rev3), 27, 448, 16}
};
const u32 mimophytbl_info_sz_rev3 =
sizeof(mimophytbl_info_rev3) / sizeof(mimophytbl_info_rev3[0]);
const u32 mimophytbl_info_sz_rev3_volatile =
sizeof(mimophytbl_info_rev3_volatile) /
sizeof(mimophytbl_info_rev3_volatile[0]);
const u32 mimophytbl_info_sz_rev3_volatile1 =
sizeof(mimophytbl_info_rev3_volatile1) /
sizeof(mimophytbl_info_rev3_volatile1[0]);
const u32 mimophytbl_info_sz_rev3_volatile2 =
sizeof(mimophytbl_info_rev3_volatile2) /
sizeof(mimophytbl_info_rev3_volatile2[0]);
const u32 mimophytbl_info_sz_rev3_volatile3 =
sizeof(mimophytbl_info_rev3_volatile3) /
sizeof(mimophytbl_info_rev3_volatile3[0]);
const u32 mimophytbl_info_sz_rev3 = ARRAY_SIZE(mimophytbl_info_rev3);
const u32 mimophytbl_info_sz_rev3_volatile = ARRAY_SIZE(mimophytbl_info_rev3_volatile);
const u32 mimophytbl_info_sz_rev3_volatile1 = ARRAY_SIZE(mimophytbl_info_rev3_volatile1);
const u32 mimophytbl_info_sz_rev3_volatile2 = ARRAY_SIZE(mimophytbl_info_rev3_volatile2);
const u32 mimophytbl_info_sz_rev3_volatile3 = ARRAY_SIZE(mimophytbl_info_rev3_volatile3);
static const u32 tmap_tbl_rev7[] = {
0x8a88aa80,
@ -10469,162 +10321,58 @@ static const u32 papd_cal_scalars_tbl_core1_rev7[] = {
};
const struct phytbl_info mimophytbl_info_rev7[] = {
{&frame_struct_rev3,
sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32}
,
{&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]),
11, 0, 16}
,
{&tmap_tbl_rev7, sizeof(tmap_tbl_rev7) / sizeof(tmap_tbl_rev7[0]), 12,
0, 32}
,
{&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]),
13, 0, 32}
,
{&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]),
14, 0, 32}
,
{&noise_var_tbl_rev7,
sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32}
,
{&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0,
16}
,
{&tdi_tbl20_ant0_rev3,
sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128,
32}
,
{&tdi_tbl20_ant1_rev3,
sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256,
32}
,
{&tdi_tbl40_ant0_rev3,
sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640,
32}
,
{&tdi_tbl40_ant1_rev3,
sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768,
32}
,
{&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]),
20, 0, 32}
,
{&chanest_tbl_rev3,
sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32}
,
{&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]),
24, 0, 8}
,
{&est_pwr_lut_core0_rev3,
sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26,
0, 8}
,
{&est_pwr_lut_core1_rev3,
sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27,
0, 8}
,
{&adj_pwr_lut_core0_rev3,
sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26,
64, 8}
,
{&adj_pwr_lut_core1_rev3,
sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27,
64, 8}
,
{&gainctrl_lut_core0_rev3,
sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]),
26, 192, 32}
,
{&gainctrl_lut_core1_rev3,
sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]),
27, 192, 32}
,
{&iq_lut_core0_rev3,
sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32}
,
{&iq_lut_core1_rev3,
sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32}
,
{&loft_lut_core0_rev3,
sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448,
16}
,
{&loft_lut_core1_rev3,
sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448,
16}
,
{&frame_struct_rev3, ARRAY_SIZE(frame_struct_rev3), 10, 0, 32},
{&pilot_tbl_rev3, ARRAY_SIZE(pilot_tbl_rev3), 11, 0, 16},
{&tmap_tbl_rev7, ARRAY_SIZE(tmap_tbl_rev7), 12, 0, 32},
{&intlv_tbl_rev3, ARRAY_SIZE(intlv_tbl_rev3), 13, 0, 32},
{&tdtrn_tbl_rev3, ARRAY_SIZE(tdtrn_tbl_rev3), 14, 0, 32},
{&noise_var_tbl_rev7, ARRAY_SIZE(noise_var_tbl_rev7), 16, 0, 32},
{&mcs_tbl_rev3, ARRAY_SIZE(mcs_tbl_rev3), 18, 0, 16},
{&tdi_tbl20_ant0_rev3, ARRAY_SIZE(tdi_tbl20_ant0_rev3), 19, 128, 32},
{&tdi_tbl20_ant1_rev3, ARRAY_SIZE(tdi_tbl20_ant1_rev3), 19, 256, 32},
{&tdi_tbl40_ant0_rev3, ARRAY_SIZE(tdi_tbl40_ant0_rev3), 19, 640, 32},
{&tdi_tbl40_ant1_rev3, ARRAY_SIZE(tdi_tbl40_ant1_rev3), 19, 768, 32},
{&pltlut_tbl_rev3, ARRAY_SIZE(pltlut_tbl_rev3), 20, 0, 32},
{&chanest_tbl_rev3, ARRAY_SIZE(chanest_tbl_rev3), 22, 0, 32},
{&frame_lut_rev3, ARRAY_SIZE(frame_lut_rev3), 24, 0, 8},
{&est_pwr_lut_core0_rev3, ARRAY_SIZE(est_pwr_lut_core0_rev3), 26, 0, 8},
{&est_pwr_lut_core1_rev3, ARRAY_SIZE(est_pwr_lut_core1_rev3), 27, 0, 8},
{&adj_pwr_lut_core0_rev3, ARRAY_SIZE(adj_pwr_lut_core0_rev3), 26, 64, 8},
{&adj_pwr_lut_core1_rev3, ARRAY_SIZE(adj_pwr_lut_core1_rev3), 27, 64, 8},
{&gainctrl_lut_core0_rev3, ARRAY_SIZE(gainctrl_lut_core0_rev3), 26, 192, 32},
{&gainctrl_lut_core1_rev3, ARRAY_SIZE(gainctrl_lut_core1_rev3), 27, 192, 32},
{&iq_lut_core0_rev3, ARRAY_SIZE(iq_lut_core0_rev3), 26, 320, 32},
{&iq_lut_core1_rev3, ARRAY_SIZE(iq_lut_core1_rev3), 27, 320, 32},
{&loft_lut_core0_rev3, ARRAY_SIZE(loft_lut_core0_rev3), 26, 448, 16},
{&loft_lut_core1_rev3, ARRAY_SIZE(loft_lut_core1_rev3), 27, 448, 16},
{&papd_comp_rfpwr_tbl_core0_rev3,
sizeof(papd_comp_rfpwr_tbl_core0_rev3) /
sizeof(papd_comp_rfpwr_tbl_core0_rev3[0]), 26, 576, 16}
,
ARRAY_SIZE(papd_comp_rfpwr_tbl_core0_rev3), 26, 576, 16},
{&papd_comp_rfpwr_tbl_core1_rev3,
sizeof(papd_comp_rfpwr_tbl_core1_rev3) /
sizeof(papd_comp_rfpwr_tbl_core1_rev3[0]), 27, 576, 16}
,
ARRAY_SIZE(papd_comp_rfpwr_tbl_core1_rev3), 27, 576, 16},
{&papd_comp_epsilon_tbl_core0_rev7,
sizeof(papd_comp_epsilon_tbl_core0_rev7) /
sizeof(papd_comp_epsilon_tbl_core0_rev7[0]), 31, 0, 32}
,
ARRAY_SIZE(papd_comp_epsilon_tbl_core0_rev7), 31, 0, 32},
{&papd_cal_scalars_tbl_core0_rev7,
sizeof(papd_cal_scalars_tbl_core0_rev7) /
sizeof(papd_cal_scalars_tbl_core0_rev7[0]), 32, 0, 32}
,
ARRAY_SIZE(papd_cal_scalars_tbl_core0_rev7), 32, 0, 32},
{&papd_comp_epsilon_tbl_core1_rev7,
sizeof(papd_comp_epsilon_tbl_core1_rev7) /
sizeof(papd_comp_epsilon_tbl_core1_rev7[0]), 33, 0, 32}
,
ARRAY_SIZE(papd_comp_epsilon_tbl_core1_rev7), 33, 0, 32},
{&papd_cal_scalars_tbl_core1_rev7,
sizeof(papd_cal_scalars_tbl_core1_rev7) /
sizeof(papd_cal_scalars_tbl_core1_rev7[0]), 34, 0, 32}
,
ARRAY_SIZE(papd_cal_scalars_tbl_core1_rev7), 34, 0, 32},
};
const u32 mimophytbl_info_sz_rev7 =
sizeof(mimophytbl_info_rev7) / sizeof(mimophytbl_info_rev7[0]);
const u32 mimophytbl_info_sz_rev7 = ARRAY_SIZE(mimophytbl_info_rev7);
const struct phytbl_info mimophytbl_info_rev16[] = {
{&noise_var_tbl_rev7,
sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32}
,
{&est_pwr_lut_core0_rev3,
sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26,
0, 8}
,
{&est_pwr_lut_core1_rev3,
sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27,
0, 8}
,
{&adj_pwr_lut_core0_rev3,
sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26,
64, 8}
,
{&adj_pwr_lut_core1_rev3,
sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27,
64, 8}
,
{&gainctrl_lut_core0_rev3,
sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]),
26, 192, 32}
,
{&gainctrl_lut_core1_rev3,
sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]),
27, 192, 32}
,
{&iq_lut_core0_rev3,
sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32}
,
{&iq_lut_core1_rev3,
sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32}
,
{&loft_lut_core0_rev3,
sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448,
16}
,
{&loft_lut_core1_rev3,
sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448,
16}
,
{&noise_var_tbl_rev7, ARRAY_SIZE(noise_var_tbl_rev7), 16, 0, 32},
{&est_pwr_lut_core0_rev3, ARRAY_SIZE(est_pwr_lut_core0_rev3), 26, 0, 8},
{&est_pwr_lut_core1_rev3, ARRAY_SIZE(est_pwr_lut_core1_rev3), 27, 0, 8},
{&adj_pwr_lut_core0_rev3, ARRAY_SIZE(adj_pwr_lut_core0_rev3), 26, 64, 8},
{&adj_pwr_lut_core1_rev3, ARRAY_SIZE(adj_pwr_lut_core1_rev3), 27, 64, 8},
{&gainctrl_lut_core0_rev3, ARRAY_SIZE(gainctrl_lut_core0_rev3), 26, 192, 32},
{&gainctrl_lut_core1_rev3, ARRAY_SIZE(gainctrl_lut_core1_rev3), 27, 192, 32},
{&iq_lut_core0_rev3, ARRAY_SIZE(iq_lut_core0_rev3), 26, 320, 32},
{&iq_lut_core1_rev3, ARRAY_SIZE(iq_lut_core1_rev3), 27, 320, 32},
{&loft_lut_core0_rev3, ARRAY_SIZE(loft_lut_core0_rev3), 26, 448, 16},
{&loft_lut_core1_rev3, ARRAY_SIZE(loft_lut_core1_rev3), 27, 448, 16},
};
const u32 mimophytbl_info_sz_rev16 =
sizeof(mimophytbl_info_rev16) / sizeof(mimophytbl_info_rev16[0]);
const u32 mimophytbl_info_sz_rev16 = ARRAY_SIZE(mimophytbl_info_rev16);

View file

@ -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

View file

@ -0,0 +1,216 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* BSD LICENSE
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/module.h>
#include <linux/stringify.h>
#include "iwl-config.h"
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
#define IWL_22000_UCODE_API_MAX 36
/* Lowest firmware API version supported */
#define IWL_22000_UCODE_API_MIN 24
/* NVM versions */
#define IWL_22000_NVM_VERSION 0x0a1d
#define IWL_22000_TX_POWER_VERSION 0xffff /* meaningless */
/* Memory offsets and lengths */
#define IWL_22000_DCCM_OFFSET 0x800000 /* LMAC1 */
#define IWL_22000_DCCM_LEN 0x10000 /* LMAC1 */
#define IWL_22000_DCCM2_OFFSET 0x880000
#define IWL_22000_DCCM2_LEN 0x8000
#define IWL_22000_SMEM_OFFSET 0x400000
#define IWL_22000_SMEM_LEN 0xD0000
#define IWL_22000_JF_FW_PRE "iwlwifi-Qu-a0-jf-b0-"
#define IWL_22000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-"
#define IWL_22000_HR_CDB_FW_PRE "iwlwifi-QuIcp-z0-hrcdb-a0-"
#define IWL_22000_HR_F0_FW_PRE "iwlwifi-QuQnj-f0-hr-a0-"
#define IWL_22000_JF_B0_FW_PRE "iwlwifi-QuQnj-a0-jf-b0-"
#define IWL_22000_HR_A0_FW_PRE "iwlwifi-QuQnj-a0-hr-a0-"
#define IWL_22000_HR_MODULE_FIRMWARE(api) \
IWL_22000_HR_FW_PRE __stringify(api) ".ucode"
#define IWL_22000_JF_MODULE_FIRMWARE(api) \
IWL_22000_JF_FW_PRE __stringify(api) ".ucode"
#define IWL_22000_HR_F0_QNJ_MODULE_FIRMWARE(api) \
IWL_22000_HR_F0_FW_PRE __stringify(api) ".ucode"
#define IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(api) \
IWL_22000_JF_B0_FW_PRE __stringify(api) ".ucode"
#define IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(api) \
IWL_22000_HR_A0_FW_PRE __stringify(api) ".ucode"
#define NVM_HW_SECTION_NUM_FAMILY_22000 10
static const struct iwl_base_params iwl_22000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_22000,
.num_of_queues = 512,
.shadow_ram_support = true,
.led_compensation = 57,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
};
static const struct iwl_ht_params iwl_22000_ht_params = {
.stbc = true,
.ldpc = true,
.ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
};
#define IWL_DEVICE_22000 \
.ucode_api_max = IWL_22000_UCODE_API_MAX, \
.ucode_api_min = IWL_22000_UCODE_API_MIN, \
.device_family = IWL_DEVICE_FAMILY_22000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl_22000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_22000, \
.non_shared_ant = ANT_A, \
.dccm_offset = IWL_22000_DCCM_OFFSET, \
.dccm_len = IWL_22000_DCCM_LEN, \
.dccm2_offset = IWL_22000_DCCM2_OFFSET, \
.dccm2_len = IWL_22000_DCCM2_LEN, \
.smem_offset = IWL_22000_SMEM_OFFSET, \
.smem_len = IWL_22000_SMEM_LEN, \
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
.apmg_not_supported = true, \
.mq_rx_supported = true, \
.vht_mu_mimo_supported = true, \
.mac_addr_from_csr = true, \
.use_tfh = true, \
.rf_id = true, \
.gen2 = true, \
.nvm_type = IWL_NVM_EXT, \
.dbgc_supported = true, \
.tx_cmd_queue_size = 32, \
.min_umac_error_event_table = 0x400000
const struct iwl_cfg iwl22000_2ac_cfg_hr = {
.name = "Intel(R) Dual Band Wireless AC 22000",
.fw_name_pre = IWL_22000_HR_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwl22000_2ac_cfg_hr_cdb = {
.name = "Intel(R) Dual Band Wireless AC 22000",
.fw_name_pre = IWL_22000_HR_CDB_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
.cdb = true,
};
const struct iwl_cfg iwl22000_2ac_cfg_jf = {
.name = "Intel(R) Dual Band Wireless AC 22000",
.fw_name_pre = IWL_22000_JF_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwl22000_2ax_cfg_hr = {
.name = "Intel(R) Dual Band Wireless AX 22000",
.fw_name_pre = IWL_22000_HR_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_f0 = {
.name = "Intel(R) Dual Band Wireless AX 22000",
.fw_name_pre = IWL_22000_HR_F0_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwl22000_2ax_cfg_qnj_jf_b0 = {
.name = "Intel(R) Dual Band Wireless AX 22000",
.fw_name_pre = IWL_22000_JF_B0_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_a0 = {
.name = "Intel(R) Dual Band Wireless AX 22000",
.fw_name_pre = IWL_22000_HR_A0_FW_PRE,
IWL_DEVICE_22000,
.ht_params = &iwl_22000_ht_params,
.nvm_ver = IWL_22000_NVM_VERSION,
.nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
MODULE_FIRMWARE(IWL_22000_HR_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_JF_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));

View file

@ -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

View file

@ -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

View file

@ -1,216 +0,0 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* BSD LICENSE
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/module.h>
#include <linux/stringify.h>
#include "iwl-config.h"
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
#define IWL_A000_UCODE_API_MAX 34
/* Lowest firmware API version supported */
#define IWL_A000_UCODE_API_MIN 24
/* NVM versions */
#define IWL_A000_NVM_VERSION 0x0a1d
#define IWL_A000_TX_POWER_VERSION 0xffff /* meaningless */
/* Memory offsets and lengths */
#define IWL_A000_DCCM_OFFSET 0x800000 /* LMAC1 */
#define IWL_A000_DCCM_LEN 0x10000 /* LMAC1 */
#define IWL_A000_DCCM2_OFFSET 0x880000
#define IWL_A000_DCCM2_LEN 0x8000
#define IWL_A000_SMEM_OFFSET 0x400000
#define IWL_A000_SMEM_LEN 0xD0000
#define IWL_A000_JF_FW_PRE "iwlwifi-Qu-a0-jf-b0-"
#define IWL_A000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-"
#define IWL_A000_HR_CDB_FW_PRE "iwlwifi-QuIcp-z0-hrcdb-a0-"
#define IWL_A000_HR_F0_FW_PRE "iwlwifi-QuQnj-f0-hr-a0-"
#define IWL_A000_JF_B0_FW_PRE "iwlwifi-QuQnj-a0-jf-b0-"
#define IWL_A000_HR_A0_FW_PRE "iwlwifi-QuQnj-a0-hr-a0-"
#define IWL_A000_HR_MODULE_FIRMWARE(api) \
IWL_A000_HR_FW_PRE __stringify(api) ".ucode"
#define IWL_A000_JF_MODULE_FIRMWARE(api) \
IWL_A000_JF_FW_PRE __stringify(api) ".ucode"
#define IWL_A000_HR_F0_QNJ_MODULE_FIRMWARE(api) \
IWL_A000_HR_F0_FW_PRE __stringify(api) ".ucode"
#define IWL_A000_JF_B0_QNJ_MODULE_FIRMWARE(api) \
IWL_A000_JF_B0_FW_PRE __stringify(api) ".ucode"
#define IWL_A000_HR_A0_QNJ_MODULE_FIRMWARE(api) \
IWL_A000_HR_A0_FW_PRE __stringify(api) ".ucode"
#define NVM_HW_SECTION_NUM_FAMILY_A000 10
static const struct iwl_base_params iwl_a000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_A000,
.num_of_queues = 512,
.shadow_ram_support = true,
.led_compensation = 57,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
};
static const struct iwl_ht_params iwl_a000_ht_params = {
.stbc = true,
.ldpc = true,
.ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ),
};
#define IWL_DEVICE_A000 \
.ucode_api_max = IWL_A000_UCODE_API_MAX, \
.ucode_api_min = IWL_A000_UCODE_API_MIN, \
.device_family = IWL_DEVICE_FAMILY_A000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl_a000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_A000, \
.non_shared_ant = ANT_A, \
.dccm_offset = IWL_A000_DCCM_OFFSET, \
.dccm_len = IWL_A000_DCCM_LEN, \
.dccm2_offset = IWL_A000_DCCM2_OFFSET, \
.dccm2_len = IWL_A000_DCCM2_LEN, \
.smem_offset = IWL_A000_SMEM_OFFSET, \
.smem_len = IWL_A000_SMEM_LEN, \
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
.apmg_not_supported = true, \
.mq_rx_supported = true, \
.vht_mu_mimo_supported = true, \
.mac_addr_from_csr = true, \
.use_tfh = true, \
.rf_id = true, \
.gen2 = true, \
.nvm_type = IWL_NVM_EXT, \
.dbgc_supported = true, \
.tx_cmd_queue_size = 32, \
.min_umac_error_event_table = 0x400000
const struct iwl_cfg iwla000_2ac_cfg_hr = {
.name = "Intel(R) Dual Band Wireless AC a000",
.fw_name_pre = IWL_A000_HR_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwla000_2ac_cfg_hr_cdb = {
.name = "Intel(R) Dual Band Wireless AC a000",
.fw_name_pre = IWL_A000_HR_CDB_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
.cdb = true,
};
const struct iwl_cfg iwla000_2ac_cfg_jf = {
.name = "Intel(R) Dual Band Wireless AC a000",
.fw_name_pre = IWL_A000_JF_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwla000_2ax_cfg_hr = {
.name = "Intel(R) Dual Band Wireless AX a000",
.fw_name_pre = IWL_A000_HR_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_f0 = {
.name = "Intel(R) Dual Band Wireless AX a000",
.fw_name_pre = IWL_A000_HR_F0_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwla000_2ax_cfg_qnj_jf_b0 = {
.name = "Intel(R) Dual Band Wireless AX a000",
.fw_name_pre = IWL_A000_JF_B0_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_a0 = {
.name = "Intel(R) Dual Band Wireless AX a000",
.fw_name_pre = IWL_A000_HR_A0_FW_PRE,
IWL_DEVICE_A000,
.ht_params = &iwl_a000_ht_params,
.nvm_ver = IWL_A000_NVM_VERSION,
.nvm_calib_ver = IWL_A000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
MODULE_FIRMWARE(IWL_A000_HR_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_A000_JF_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_A000_HR_F0_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_A000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_A000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX));

View file

@ -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 */

View file

@ -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;

View file

@ -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,

View file

@ -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
*/

View file

@ -62,6 +62,267 @@
#include "mac.h"
/**
* enum iwl_tlc_mng_cfg_flags_enum - options for TLC config flags
* @IWL_TLC_MNG_CFG_FLAGS_CCK_MSK: CCK support
* @IWL_TLC_MNG_CFG_FLAGS_DD_MSK: enable DD
* @IWL_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC
* @IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK: enable LDPC
* @IWL_TLC_MNG_CFG_FLAGS_BF_MSK: enable BFER
* @IWL_TLC_MNG_CFG_FLAGS_DCM_MSK: enable DCM
*/
enum iwl_tlc_mng_cfg_flags_enum {
IWL_TLC_MNG_CFG_FLAGS_CCK_MSK = BIT(0),
IWL_TLC_MNG_CFG_FLAGS_DD_MSK = BIT(1),
IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(2),
IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK = BIT(3),
IWL_TLC_MNG_CFG_FLAGS_BF_MSK = BIT(4),
IWL_TLC_MNG_CFG_FLAGS_DCM_MSK = BIT(5),
};
/**
* enum iwl_tlc_mng_cfg_cw_enum - channel width options
* @IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ: 20MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ: 40MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ: 80MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ: 160MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_LAST: maximum value
*/
enum iwl_tlc_mng_cfg_cw_enum {
IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_LAST = IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ,
};
/**
* enum iwl_tlc_mng_cfg_chains_enum - possible chains
* @IWL_TLC_MNG_CHAIN_A_MSK: chain A
* @IWL_TLC_MNG_CHAIN_B_MSK: chain B
* @IWL_TLC_MNG_CHAIN_C_MSK: chain C
*/
enum iwl_tlc_mng_cfg_chains_enum {
IWL_TLC_MNG_CHAIN_A_MSK = BIT(0),
IWL_TLC_MNG_CHAIN_B_MSK = BIT(1),
IWL_TLC_MNG_CHAIN_C_MSK = BIT(2),
};
/**
* enum iwl_tlc_mng_cfg_gi_enum - guard interval options
* @IWL_TLC_MNG_SGI_20MHZ_MSK: enable short GI for 20MHZ
* @IWL_TLC_MNG_SGI_40MHZ_MSK: enable short GI for 40MHZ
* @IWL_TLC_MNG_SGI_80MHZ_MSK: enable short GI for 80MHZ
* @IWL_TLC_MNG_SGI_160MHZ_MSK: enable short GI for 160MHZ
*/
enum iwl_tlc_mng_cfg_gi_enum {
IWL_TLC_MNG_SGI_20MHZ_MSK = BIT(0),
IWL_TLC_MNG_SGI_40MHZ_MSK = BIT(1),
IWL_TLC_MNG_SGI_80MHZ_MSK = BIT(2),
IWL_TLC_MNG_SGI_160MHZ_MSK = BIT(3),
};
/**
* enum iwl_tlc_mng_cfg_mode_enum - supported modes
* @IWL_TLC_MNG_MODE_CCK: enable CCK
* @IWL_TLC_MNG_MODE_OFDM_NON_HT: enable OFDM (non HT)
* @IWL_TLC_MNG_MODE_NON_HT: enable non HT
* @IWL_TLC_MNG_MODE_HT: enable HT
* @IWL_TLC_MNG_MODE_VHT: enable VHT
* @IWL_TLC_MNG_MODE_HE: enable HE
* @IWL_TLC_MNG_MODE_INVALID: invalid value
* @IWL_TLC_MNG_MODE_NUM: a count of possible modes
*/
enum iwl_tlc_mng_cfg_mode_enum {
IWL_TLC_MNG_MODE_CCK = 0,
IWL_TLC_MNG_MODE_OFDM_NON_HT = IWL_TLC_MNG_MODE_CCK,
IWL_TLC_MNG_MODE_NON_HT = IWL_TLC_MNG_MODE_CCK,
IWL_TLC_MNG_MODE_HT,
IWL_TLC_MNG_MODE_VHT,
IWL_TLC_MNG_MODE_HE,
IWL_TLC_MNG_MODE_INVALID,
IWL_TLC_MNG_MODE_NUM = IWL_TLC_MNG_MODE_INVALID,
};
/**
* enum iwl_tlc_mng_vht_he_types_enum - VHT HE types
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU: VHT HT single user
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT: VHT HT single user extended
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU: VHT HT multiple users
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED: trigger based
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM: a count of possible types
*/
enum iwl_tlc_mng_vht_he_types_enum {
IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU = 0,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM =
IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED,
};
/**
* enum iwl_tlc_mng_ht_rates_enum - HT/VHT rates
* @IWL_TLC_MNG_HT_RATE_MCS0: index of MCS0
* @IWL_TLC_MNG_HT_RATE_MCS1: index of MCS1
* @IWL_TLC_MNG_HT_RATE_MCS2: index of MCS2
* @IWL_TLC_MNG_HT_RATE_MCS3: index of MCS3
* @IWL_TLC_MNG_HT_RATE_MCS4: index of MCS4
* @IWL_TLC_MNG_HT_RATE_MCS5: index of MCS5
* @IWL_TLC_MNG_HT_RATE_MCS6: index of MCS6
* @IWL_TLC_MNG_HT_RATE_MCS7: index of MCS7
* @IWL_TLC_MNG_HT_RATE_MCS8: index of MCS8
* @IWL_TLC_MNG_HT_RATE_MCS9: index of MCS9
* @IWL_TLC_MNG_HT_RATE_MAX: maximal rate for HT/VHT
*/
enum iwl_tlc_mng_ht_rates_enum {
IWL_TLC_MNG_HT_RATE_MCS0 = 0,
IWL_TLC_MNG_HT_RATE_MCS1,
IWL_TLC_MNG_HT_RATE_MCS2,
IWL_TLC_MNG_HT_RATE_MCS3,
IWL_TLC_MNG_HT_RATE_MCS4,
IWL_TLC_MNG_HT_RATE_MCS5,
IWL_TLC_MNG_HT_RATE_MCS6,
IWL_TLC_MNG_HT_RATE_MCS7,
IWL_TLC_MNG_HT_RATE_MCS8,
IWL_TLC_MNG_HT_RATE_MCS9,
IWL_TLC_MNG_HT_RATE_MAX = IWL_TLC_MNG_HT_RATE_MCS9,
};
/* Maximum supported tx antennas number */
#define MAX_RS_ANT_NUM 3
/**
* struct tlc_config_cmd - TLC configuration
* @sta_id: station id
* @reserved1: reserved
* @max_supp_ch_width: channel width
* @flags: bitmask of %IWL_TLC_MNG_CONFIG_FLAGS_ENABLE_\*
* @chains: bitmask of %IWL_TLC_MNG_CHAIN_\*
* @max_supp_ss: valid values are 0-3, 0 - spatial streams are not supported
* @valid_vht_he_types: bitmap of %IWL_TLC_MNG_VALID_VHT_HE_TYPES_\*
* @non_ht_supp_rates: bitmap of supported legacy rates
* @ht_supp_rates: bitmap of supported HT/VHT rates, valid bits are 0-9
* @mode: modulation type %IWL_TLC_MNG_MODE_\*
* @reserved2: reserved
* @he_supp_rates: bitmap of supported HE rates
* @sgi_ch_width_supp: bitmap of SGI support per channel width
* @he_gi_support: 11ax HE guard interval
* @max_ampdu_cnt: max AMPDU size (frames count)
*/
struct iwl_tlc_config_cmd {
u8 sta_id;
u8 reserved1[3];
u8 max_supp_ch_width;
u8 chains;
u8 max_supp_ss;
u8 valid_vht_he_types;
__le16 flags;
__le16 non_ht_supp_rates;
__le16 ht_supp_rates[MAX_RS_ANT_NUM];
u8 mode;
u8 reserved2;
__le16 he_supp_rates;
u8 sgi_ch_width_supp;
u8 he_gi_support;
__le32 max_ampdu_cnt;
} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_1 */
#define IWL_TLC_NOTIF_INIT_RATE_POS 0
#define IWL_TLC_NOTIF_INIT_RATE_MSK BIT(IWL_TLC_NOTIF_INIT_RATE_POS)
#define IWL_TLC_NOTIF_REQ_INTERVAL (500)
/**
* struct iwl_tlc_notif_req_config_cmd - request notif on specific changes
* @sta_id: relevant station
* @reserved1: reserved
* @flags: bitmap of requested notifications %IWL_TLC_NOTIF_INIT_\*
* @interval: minimum time between notifications from TLC to the driver (msec)
* @reserved2: reserved
*/
struct iwl_tlc_notif_req_config_cmd {
u8 sta_id;
u8 reserved1;
__le16 flags;
__le16 interval;
__le16 reserved2;
} __packed; /* TLC_MNG_NOTIF_REQ_CMD_API_S_VER_1 */
/**
* struct iwl_tlc_update_notif - TLC notification from FW
* @sta_id: station id
* @reserved: reserved
* @flags: bitmap of notifications reported
* @values: field per flag in struct iwl_tlc_notif_req_config_cmd
*/
struct iwl_tlc_update_notif {
u8 sta_id;
u8 reserved;
__le16 flags;
__le32 values[16];
} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_1 */
/**
* enum iwl_tlc_debug_flags - debug options
* @IWL_TLC_DEBUG_FIXED_RATE: set fixed rate for rate scaling
* @IWL_TLC_DEBUG_STATS_TH: threshold for sending statistics to the driver, in
* frames
* @IWL_TLC_DEBUG_STATS_TIME_TH: threshold for sending statistics to the
* driver, in msec
* @IWL_TLC_DEBUG_AGG_TIME_LIM: time limit for a BA session
* @IWL_TLC_DEBUG_AGG_DIS_START_TH: frame with try-count greater than this
* threshold should not start an aggregation session
* @IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM: set max number of frames in an aggregation
* @IWL_TLC_DEBUG_RENEW_ADDBA_DELAY: delay between retries of ADD BA
* @IWL_TLC_DEBUG_START_AC_RATE_IDX: frames per second to start a BA session
* @IWL_TLC_DEBUG_NO_FAR_RANGE_TWEAK: disable BW scaling
*/
enum iwl_tlc_debug_flags {
IWL_TLC_DEBUG_FIXED_RATE,
IWL_TLC_DEBUG_STATS_TH,
IWL_TLC_DEBUG_STATS_TIME_TH,
IWL_TLC_DEBUG_AGG_TIME_LIM,
IWL_TLC_DEBUG_AGG_DIS_START_TH,
IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM,
IWL_TLC_DEBUG_RENEW_ADDBA_DELAY,
IWL_TLC_DEBUG_START_AC_RATE_IDX,
IWL_TLC_DEBUG_NO_FAR_RANGE_TWEAK,
}; /* TLC_MNG_DEBUG_FLAGS_API_E_VER_1 */
/**
* struct iwl_dhc_tlc_dbg - fixed debug config
* @sta_id: bit 0 - enable/disable, bits 1 - 7 hold station id
* @reserved1: reserved
* @flags: bitmap of %IWL_TLC_DEBUG_\*
* @fixed_rate: rate value
* @stats_threshold: if number of tx-ed frames is greater, send statistics
* @time_threshold: statistics threshold in usec
* @agg_time_lim: max agg time
* @agg_dis_start_threshold: frames with try-cont greater than this count will
* not be aggregated
* @agg_frame_count_lim: agg size
* @addba_retry_delay: delay between retries of ADD BA
* @start_ac_rate_idx: frames per second to start a BA session
* @no_far_range_tweak: disable BW scaling
* @reserved2: reserved
*/
struct iwl_dhc_tlc_cmd {
u8 sta_id;
u8 reserved1[3];
__le32 flags;
__le32 fixed_rate;
__le16 stats_threshold;
__le16 time_threshold;
__le16 agg_time_lim;
__le16 agg_dis_start_threshold;
__le16 agg_frame_count_lim;
__le16 addba_retry_delay;
u8 start_ac_rate_idx[IEEE80211_NUM_ACS];
u8 no_far_range_tweak;
u8 reserved2[3];
} __packed;
/*
* These serve as indexes into
* struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
@ -253,7 +514,6 @@ enum {
#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \
RATE_MCS_ANT_C_MSK)
#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK
#define RATE_MCS_ANT_NUM 3
/* Bit 17: (0) SS, (1) SS*2 */
#define RATE_MCS_STBC_POS 17

View file

@ -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

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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__ */

View file

@ -919,9 +919,14 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
minor = le32_to_cpup(ptr++);
local_comp = le32_to_cpup(ptr);
snprintf(drv->fw.fw_version,
sizeof(drv->fw.fw_version), "%u.%u.%u",
major, minor, local_comp);
if (major >= 35)
snprintf(drv->fw.fw_version,
sizeof(drv->fw.fw_version),
"%u.%08x.%u", major, minor, local_comp);
else
snprintf(drv->fw.fw_version,
sizeof(drv->fw.fw_version),
"%u.%u.%u", major, minor, local_comp);
break;
}
case IWL_UCODE_TLV_FW_DBG_DEST: {

View file

@ -121,7 +121,7 @@
#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20)
#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80)
/* a000 TFD table address, 64 bit */
/* 22000 TFD table address, 64 bit */
#define TFH_TFDQ_CBB_TABLE (0x1C00)
/* Find TFD CB base pointer for given queue */
@ -140,7 +140,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans,
return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20);
}
/* a000 configuration registers */
/* 22000 configuration registers */
/*
* TFH Configuration register.
@ -697,8 +697,8 @@ struct iwl_tfh_tb {
* Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM.
* Both driver and device share these circular buffers, each of which must be
* contiguous 256 TFDs.
* For pre a000 HW it is 256 x 128 bytes-per-TFD = 32 KBytes
* For a000 HW and on it is 256 x 256 bytes-per-TFD = 65 KBytes
* For pre 22000 HW it is 256 x 128 bytes-per-TFD = 32 KBytes
* For 22000 HW and on it is 256 x 256 bytes-per-TFD = 65 KBytes
*
* Driver must indicate the physical address of the base of each
* circular buffer via the FH_MEM_CBBC_QUEUE registers.
@ -750,10 +750,10 @@ struct iwl_tfh_tfd {
/**
* struct iwlagn_schedq_bc_tbl scheduler byte count table
* base physical address provided by SCD_DRAM_BASE_ADDR
* For devices up to a000:
* For devices up to 22000:
* @tfd_offset 0-12 - tx command byte count
* 12-16 - station index
* For a000 and on:
* For 22000 and on:
* @tfd_offset 0-12 - tx command byte count
* 12-13 - number of 64 byte chunks
* 14-16 - reserved

View file

@ -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,

View file

@ -2,7 +2,7 @@
obj-$(CONFIG_IWLMVM) += iwlmvm.o
iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o rxmq.o tx.o binding.o quota.o sta.o sf.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += scan.o time-event.o rs.o rs-fw.o
iwlmvm-y += power.o coex.o
iwlmvm-y += tt.o offloading.o tdls.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o

View file

@ -7,7 +7,6 @@
*
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@ -34,7 +33,6 @@
*
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -514,36 +512,17 @@ void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
if (!iwl_mvm_has_new_ats_coex_api(mvm)) {
struct iwl_bt_coex_profile_notif_v4 *v4 = (void *)pkt->data;
mvm->last_bt_notif.mbox_msg[0] = v4->mbox_msg[0];
mvm->last_bt_notif.mbox_msg[1] = v4->mbox_msg[1];
mvm->last_bt_notif.mbox_msg[2] = v4->mbox_msg[2];
mvm->last_bt_notif.mbox_msg[3] = v4->mbox_msg[3];
mvm->last_bt_notif.msg_idx = v4->msg_idx;
mvm->last_bt_notif.bt_ci_compliance = v4->bt_ci_compliance;
mvm->last_bt_notif.primary_ch_lut = v4->primary_ch_lut;
mvm->last_bt_notif.secondary_ch_lut = v4->secondary_ch_lut;
mvm->last_bt_notif.bt_activity_grading =
v4->bt_activity_grading;
mvm->last_bt_notif.ttc_status = v4->ttc_status;
mvm->last_bt_notif.rrc_status = v4->rrc_status;
} else {
/* save this notification for future use: rssi fluctuations */
memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
}
IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n",
mvm->last_bt_notif.bt_ci_compliance);
IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
le32_to_cpu(mvm->last_bt_notif.primary_ch_lut));
le32_to_cpu(notif->primary_ch_lut));
IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut));
le32_to_cpu(notif->secondary_ch_lut));
IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
le32_to_cpu(mvm->last_bt_notif.bt_activity_grading));
le32_to_cpu(notif->bt_activity_grading));
/* remember this notification for future use: rssi fluctuations */
memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
iwl_mvm_bt_coex_notif_handle(mvm);
}

View file

@ -429,231 +429,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
return err;
}
enum iwl_mvm_tcp_packet_type {
MVM_TCP_TX_SYN,
MVM_TCP_RX_SYNACK,
MVM_TCP_TX_DATA,
MVM_TCP_RX_ACK,
MVM_TCP_RX_WAKE,
MVM_TCP_TX_FIN,
};
static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
{
__sum16 check = tcp_v4_check(len, saddr, daddr, 0);
return cpu_to_le16(be16_to_cpu((__force __be16)check));
}
static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif,
struct cfg80211_wowlan_tcp *tcp,
void *_pkt, u8 *mask,
__le16 *pseudo_hdr_csum,
enum iwl_mvm_tcp_packet_type ptype)
{
struct {
struct ethhdr eth;
struct iphdr ip;
struct tcphdr tcp;
u8 data[];
} __packed *pkt = _pkt;
u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
int i;
pkt->eth.h_proto = cpu_to_be16(ETH_P_IP),
pkt->ip.version = 4;
pkt->ip.ihl = 5;
pkt->ip.protocol = IPPROTO_TCP;
switch (ptype) {
case MVM_TCP_TX_SYN:
case MVM_TCP_TX_DATA:
case MVM_TCP_TX_FIN:
memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN);
memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN);
pkt->ip.ttl = 128;
pkt->ip.saddr = tcp->src;
pkt->ip.daddr = tcp->dst;
pkt->tcp.source = cpu_to_be16(tcp->src_port);
pkt->tcp.dest = cpu_to_be16(tcp->dst_port);
/* overwritten for TX SYN later */
pkt->tcp.doff = sizeof(struct tcphdr) / 4;
pkt->tcp.window = cpu_to_be16(65000);
break;
case MVM_TCP_RX_SYNACK:
case MVM_TCP_RX_ACK:
case MVM_TCP_RX_WAKE:
memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN);
memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN);
pkt->ip.saddr = tcp->dst;
pkt->ip.daddr = tcp->src;
pkt->tcp.source = cpu_to_be16(tcp->dst_port);
pkt->tcp.dest = cpu_to_be16(tcp->src_port);
break;
default:
WARN_ON(1);
return;
}
switch (ptype) {
case MVM_TCP_TX_SYN:
/* firmware assumes 8 option bytes - 8 NOPs for now */
memset(pkt->data, 0x01, 8);
ip_tot_len += 8;
pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4;
pkt->tcp.syn = 1;
break;
case MVM_TCP_TX_DATA:
ip_tot_len += tcp->payload_len;
memcpy(pkt->data, tcp->payload, tcp->payload_len);
pkt->tcp.psh = 1;
pkt->tcp.ack = 1;
break;
case MVM_TCP_TX_FIN:
pkt->tcp.fin = 1;
pkt->tcp.ack = 1;
break;
case MVM_TCP_RX_SYNACK:
pkt->tcp.syn = 1;
pkt->tcp.ack = 1;
break;
case MVM_TCP_RX_ACK:
pkt->tcp.ack = 1;
break;
case MVM_TCP_RX_WAKE:
ip_tot_len += tcp->wake_len;
pkt->tcp.psh = 1;
pkt->tcp.ack = 1;
memcpy(pkt->data, tcp->wake_data, tcp->wake_len);
break;
}
switch (ptype) {
case MVM_TCP_TX_SYN:
case MVM_TCP_TX_DATA:
case MVM_TCP_TX_FIN:
pkt->ip.tot_len = cpu_to_be16(ip_tot_len);
pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl);
break;
case MVM_TCP_RX_WAKE:
for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) {
u8 tmp = tcp->wake_mask[i];
mask[i + 6] |= tmp << 6;
if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8))
mask[i + 7] = tmp >> 2;
}
/* fall through for ethernet/IP/TCP headers mask */
case MVM_TCP_RX_SYNACK:
case MVM_TCP_RX_ACK:
mask[0] = 0xff; /* match ethernet */
/*
* match ethernet, ip.version, ip.ihl
* the ip.ihl half byte is really masked out by firmware
*/
mask[1] = 0x7f;
mask[2] = 0x80; /* match ip.protocol */
mask[3] = 0xfc; /* match ip.saddr, ip.daddr */
mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */
mask[5] = 0x80; /* match tcp flags */
/* leave rest (0 or set for MVM_TCP_RX_WAKE) */
break;
};
*pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr),
pkt->ip.saddr, pkt->ip.daddr);
}
static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_wowlan_tcp *tcp)
{
struct iwl_wowlan_remote_wake_config *cfg;
struct iwl_host_cmd cmd = {
.id = REMOTE_WAKE_CONFIG_CMD,
.len = { sizeof(*cfg), },
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
int ret;
if (!tcp)
return 0;
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
cmd.data[0] = cfg;
cfg->max_syn_retries = 10;
cfg->max_data_retries = 10;
cfg->tcp_syn_ack_timeout = 1; /* seconds */
cfg->tcp_ack_timeout = 1; /* seconds */
/* SYN (TX) */
iwl_mvm_build_tcp_packet(
vif, tcp, cfg->syn_tx.data, NULL,
&cfg->syn_tx.info.tcp_pseudo_header_checksum,
MVM_TCP_TX_SYN);
cfg->syn_tx.info.tcp_payload_length = 0;
/* SYN/ACK (RX) */
iwl_mvm_build_tcp_packet(
vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
&cfg->synack_rx.info.tcp_pseudo_header_checksum,
MVM_TCP_RX_SYNACK);
cfg->synack_rx.info.tcp_payload_length = 0;
/* KEEPALIVE/ACK (TX) */
iwl_mvm_build_tcp_packet(
vif, tcp, cfg->keepalive_tx.data, NULL,
&cfg->keepalive_tx.info.tcp_pseudo_header_checksum,
MVM_TCP_TX_DATA);
cfg->keepalive_tx.info.tcp_payload_length =
cpu_to_le16(tcp->payload_len);
cfg->sequence_number_offset = tcp->payload_seq.offset;
/* length must be 0..4, the field is little endian */
cfg->sequence_number_length = tcp->payload_seq.len;
cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start);
cfg->keepalive_interval = cpu_to_le16(tcp->data_interval);
if (tcp->payload_tok.len) {
cfg->token_offset = tcp->payload_tok.offset;
cfg->token_length = tcp->payload_tok.len;
cfg->num_tokens =
cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len);
memcpy(cfg->tokens, tcp->payload_tok.token_stream,
tcp->tokens_size);
} else {
/* set tokens to max value to almost never run out */
cfg->num_tokens = cpu_to_le16(65535);
}
/* ACK (RX) */
iwl_mvm_build_tcp_packet(
vif, tcp, cfg->keepalive_ack_rx.data,
cfg->keepalive_ack_rx.rx_mask,
&cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum,
MVM_TCP_RX_ACK);
cfg->keepalive_ack_rx.info.tcp_payload_length = 0;
/* WAKEUP (RX) */
iwl_mvm_build_tcp_packet(
vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
&cfg->wake_rx.info.tcp_pseudo_header_checksum,
MVM_TCP_RX_WAKE);
cfg->wake_rx.info.tcp_payload_length =
cpu_to_le16(tcp->wake_len);
/* FIN */
iwl_mvm_build_tcp_packet(
vif, tcp, cfg->fin_tx.data, NULL,
&cfg->fin_tx.info.tcp_pseudo_header_checksum,
MVM_TCP_TX_FIN);
cfg->fin_tx.info.tcp_payload_length = 0;
ret = iwl_mvm_send_cmd(mvm, &cmd);
kfree(cfg);
return ret;
}
static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *ap_sta)
{
@ -1082,12 +857,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
if (ret)
return ret;
ret = iwl_mvm_send_proto_offload(mvm, vif, false, true, 0);
if (ret)
return ret;
ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
return ret;
return iwl_mvm_send_proto_offload(mvm, vif, false, true, 0);
}
static int

View file

@ -425,6 +425,50 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
static ssize_t iwl_dbgfs_rs_data_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta = file->private_data;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
struct iwl_mvm *mvm = lq_sta->pers.drv;
static const size_t bufsz = 2048;
char *buff;
int desc = 0;
ssize_t ret;
buff = kmalloc(bufsz, GFP_KERNEL);
if (!buff)
return -ENOMEM;
mutex_lock(&mvm->mutex);
desc += scnprintf(buff + desc, bufsz - desc, "sta_id %d\n",
lq_sta->pers.sta_id);
desc += scnprintf(buff + desc, bufsz - desc,
"fixed rate 0x%X\n",
lq_sta->pers.dbg_fixed_rate);
desc += scnprintf(buff + desc, bufsz - desc,
"A-MPDU size limit %d\n",
lq_sta->pers.dbg_agg_frame_count_lim);
desc += scnprintf(buff + desc, bufsz - desc,
"valid_tx_ant %s%s%s\n",
(iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",
(iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "",
(iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : "");
desc += scnprintf(buff + desc, bufsz - desc,
"last tx rate=0x%X ",
lq_sta->last_rate_n_flags);
desc += rs_pretty_print_rate(buff + desc, bufsz - desc,
lq_sta->last_rate_n_flags);
mutex_unlock(&mvm->mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
kfree(buff);
return ret;
}
static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@ -470,8 +514,7 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
}
static
int iwl_mvm_coex_dump_mbox(struct iwl_mvm *mvm,
struct iwl_bt_coex_profile_notif *notif, char *buf,
int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
int pos, int bufsz)
{
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
@ -525,12 +568,7 @@ int iwl_mvm_coex_dump_mbox(struct iwl_mvm *mvm,
BT_MBOX_PRINT(3, INBAND_P, false);
BT_MBOX_PRINT(3, MSG_TYPE_2, false);
BT_MBOX_PRINT(3, SSN_2, false);
BT_MBOX_PRINT(3, UPDATE_REQUEST, !iwl_mvm_has_new_ats_coex_api(mvm));
if (iwl_mvm_has_new_ats_coex_api(mvm)) {
BT_MBOX_PRINT(4, ATS_BT_INTERVAL, false);
BT_MBOX_PRINT(4, ATS_BT_ACTIVE_MAX_TH, true);
}
BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
return pos;
}
@ -549,7 +587,7 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
mutex_lock(&mvm->mutex);
pos += iwl_mvm_coex_dump_mbox(mvm, notif, buf, pos, bufsz);
pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
pos += scnprintf(buf + pos, bufsz - pos, "bt_ci_compliance = %d\n",
notif->bt_ci_compliance);
@ -721,6 +759,9 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
mutex_lock(&mvm->mutex);
if (iwl_mvm_firmware_running(mvm))
iwl_mvm_request_statistics(mvm, false);
pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
"Statistics_Rx - OFDM");
if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
@ -936,7 +977,8 @@ static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm,
continue;
pos += scnprintf(pos, endpos - pos, "Rate[%d]: ",
(int)(ARRAY_SIZE(stats->last_rates) - i));
pos += rs_pretty_print_rate(pos, stats->last_rates[idx]);
pos += rs_pretty_print_rate(pos, endpos - pos,
stats->last_rates[idx]);
}
spin_unlock_bh(&mvm->drv_stats_lock);
@ -1603,6 +1645,19 @@ static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
#define MVM_DEBUGFS_WRITE_STA_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_sta)
#define MVM_DEBUGFS_READ_WRITE_STA_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_sta)
#define MVM_DEBUGFS_ADD_STA_FILE_ALIAS(alias, name, parent, mode) do { \
if (!debugfs_create_file(alias, mode, parent, sta, \
&iwl_dbgfs_##name##_ops)) \
goto err; \
} while (0)
#define MVM_DEBUGFS_ADD_STA_FILE(name, parent, mode) \
MVM_DEBUGFS_ADD_STA_FILE_ALIAS(#name, name, parent, mode)
static ssize_t
iwl_dbgfs_prph_reg_read(struct file *file,
char __user *user_buf,
@ -1687,6 +1742,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64);
MVM_DEBUGFS_READ_FILE_OPS(nic_temp);
MVM_DEBUGFS_READ_FILE_OPS(stations);
MVM_DEBUGFS_READ_FILE_OPS(rs_data);
MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64);
@ -1851,6 +1907,21 @@ static const struct file_operations iwl_dbgfs_mem_ops = {
.llseek = default_llseek,
};
void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct dentry *dir)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD))
MVM_DEBUGFS_ADD_STA_FILE(rs_data, dir, S_IRUSR);
return;
err:
IWL_ERR(mvm, "Can't create the mvm station debugfs entry\n");
}
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
struct dentry *bcast_dir __maybe_unused;

View file

@ -297,7 +297,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
if (ret) {
struct iwl_trans *trans = mvm->trans;
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_A000)
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_22000)
IWL_ERR(mvm,
"SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
iwl_read_prph(trans, UMAG_SB_CPU_1_STATUS),
@ -923,11 +923,11 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm)
ret = iwl_run_init_mvm_ucode(mvm, false);
if (iwlmvm_mod_params.init_dbg)
return 0;
if (ret) {
IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
if (iwlmvm_mod_params.init_dbg)
return 0;
return ret;
}
@ -998,9 +998,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
goto error;
/* Init RSS configuration */
/* TODO - remove a000 disablement when we have RXQ config API */
/* TODO - remove 22000 disablement when we have RXQ config API */
if (iwl_mvm_has_new_rx_api(mvm) &&
mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_A000) {
mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_22000) {
ret = iwl_send_rss_cfg_cmd(mvm);
if (ret) {
IWL_ERR(mvm, "Failed to configure RSS queues: %d\n",
@ -1111,7 +1111,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
return 0;
error:
if (!iwlmvm_mod_params.init_dbg)
if (!iwlmvm_mod_params.init_dbg || !ret)
iwl_mvm_stop_device(mvm);
return ret;
}

View file

@ -114,29 +114,6 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
},
};
#ifdef CONFIG_PM_SLEEP
static const struct nl80211_wowlan_tcp_data_token_feature
iwl_mvm_wowlan_tcp_token_feature = {
.min_len = 0,
.max_len = 255,
.bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS,
};
static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
.tok = &iwl_mvm_wowlan_tcp_token_feature,
.data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN -
sizeof(struct ethhdr) -
sizeof(struct iphdr) -
sizeof(struct tcphdr),
.data_interval_max = 65535, /* __le16 in API */
.wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN -
sizeof(struct ethhdr) -
sizeof(struct iphdr) -
sizeof(struct tcphdr),
.seq = true,
};
#endif
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
/*
* Use the reserved field to indicate magic values.
@ -443,6 +420,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD)) {
ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
}
if (iwl_mvm_has_new_rx_api(mvm))
ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
@ -477,7 +460,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
/* this is the case for CCK frames, it's better (only 8) for OFDM */
hw->radiotap_timestamp.accuracy = 22;
hw->rate_control_algorithm = "iwl-mvm-rs";
if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD))
hw->rate_control_algorithm = RS_NAME;
hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
@ -702,7 +687,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
hw->wiphy->wowlan = &mvm->wowlan;
}
#endif
@ -3216,6 +3200,10 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
duration, type);
/*
* Flush the done work, just in case it's still pending, so that
* the work it does can complete and we can accept new frames.
*/
flush_work(&mvm->roc_done_wk);
mutex_lock(&mvm->mutex);
@ -4301,7 +4289,7 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
mvm->trans->num_rx_queues);
/* TODO - remove this when we have RXQ config API */
if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_A000) {
if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_22000) {
qmask = BIT(0);
if (notif->sync)
atomic_set(&mvm->queue_sync_counter, 1);
@ -4414,4 +4402,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
#endif
.get_survey = iwl_mvm_mac_get_survey,
.sta_statistics = iwl_mvm_mac_sta_statistics,
#ifdef CONFIG_IWLWIFI_DEBUGFS
.sta_add_debugfs = iwl_mvm_sta_add_debugfs,
#endif
};

View file

@ -1248,7 +1248,7 @@ static inline bool iwl_mvm_has_new_tx_api(struct iwl_mvm *mvm)
static inline bool iwl_mvm_has_unified_ucode(struct iwl_mvm *mvm)
{
/* TODO - better define this */
return mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_A000;
return mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000;
}
static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm)
@ -1272,12 +1272,6 @@ static inline bool iwl_mvm_has_new_rx_stats_api(struct iwl_mvm *mvm)
IWL_UCODE_TLV_API_NEW_RX_STATS);
}
static inline bool iwl_mvm_has_new_ats_coex_api(struct iwl_mvm *mvm)
{
return fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL);
}
static inline bool iwl_mvm_has_quota_low_latency(struct iwl_mvm *mvm)
{
return fw_has_api(&mvm->fw->ucode_capa,
@ -1600,9 +1594,9 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
/* rate scaling */
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg);
int rs_pretty_print_rate(char *buf, const u32 rate);
int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate);
void rs_update_last_rssi(struct iwl_mvm *mvm,
struct iwl_lq_sta *lq_sta,
struct iwl_mvm_sta *mvmsta,
struct ieee80211_rx_status *rx_status);
/* power management */
@ -1882,5 +1876,11 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b);
int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm);
#ifdef CONFIG_IWLWIFI_DEBUGFS
void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct dentry *dir);
#endif
#endif /* __IWL_MVM_H__ */

View file

@ -127,11 +127,8 @@ static int __init iwl_mvm_init(void)
}
ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
if (ret) {
if (ret)
pr_err("Unable to register MVM op_mode: %d\n", ret);
iwl_mvm_rate_control_unregister();
}
return ret;
}
@ -750,7 +747,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
mutex_lock(&mvm->mutex);
iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE);
err = iwl_run_init_mvm_ucode(mvm, true);
if (!iwlmvm_mod_params.init_dbg)
if (!iwlmvm_mod_params.init_dbg || !err)
iwl_mvm_stop_device(mvm);
iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE);
mutex_unlock(&mvm->mutex);
@ -1021,6 +1018,8 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
iwl_mvm_rx_queue_notif(mvm, rxb, 0);
else if (cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))
iwl_mvm_rx_frame_release(mvm, napi, rxb, 0);
else if (cmd == WIDE_ID(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF))
iwl_mvm_tlc_update_notif(mvm, pkt);
else
iwl_mvm_rx_common(mvm, rxb, pkt);
}

View file

@ -0,0 +1,314 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include "rs.h"
#include "fw-api.h"
#include "sta.h"
#include "iwl-op-mode.h"
#include "mvm.h"
static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta)
{
switch (sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
return IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ;
case IEEE80211_STA_RX_BW_80:
return IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ;
case IEEE80211_STA_RX_BW_40:
return IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ;
case IEEE80211_STA_RX_BW_20:
default:
return IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ;
}
}
static u8 rs_fw_set_active_chains(u8 chains)
{
u8 fw_chains = 0;
if (chains & ANT_A)
fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK;
if (chains & ANT_B)
fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
if (chains & ANT_C)
fw_chains |= IWL_TLC_MNG_CHAIN_C_MSK;
return fw_chains;
}
static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)
{
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
u8 supp = 0;
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
supp |= IWL_TLC_MNG_SGI_20MHZ_MSK;
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
supp |= IWL_TLC_MNG_SGI_40MHZ_MSK;
if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
supp |= IWL_TLC_MNG_SGI_80MHZ_MSK;
if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
supp |= IWL_TLC_MNG_SGI_160MHZ_MSK;
return supp;
}
static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
bool vht_ena = vht_cap && vht_cap->vht_supported;
u16 flags = IWL_TLC_MNG_CFG_FLAGS_CCK_MSK |
IWL_TLC_MNG_CFG_FLAGS_DCM_MSK |
IWL_TLC_MNG_CFG_FLAGS_DD_MSK;
if (mvm->cfg->ht_params->stbc &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) ||
(vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))))
flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
if (mvm->cfg->ht_params->ldpc &&
((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) ||
(vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
(vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
flags |= IWL_TLC_MNG_CFG_FLAGS_BF_MSK;
return flags;
}
static
int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
int nss)
{
u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
(0x3 << (2 * (nss - 1)));
rx_mcs >>= (2 * (nss - 1));
switch (rx_mcs) {
case IEEE80211_VHT_MCS_SUPPORT_0_7:
return IWL_TLC_MNG_HT_RATE_MCS7;
case IEEE80211_VHT_MCS_SUPPORT_0_8:
return IWL_TLC_MNG_HT_RATE_MCS8;
case IEEE80211_VHT_MCS_SUPPORT_0_9:
return IWL_TLC_MNG_HT_RATE_MCS9;
default:
WARN_ON_ONCE(1);
break;
}
return 0;
}
static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta,
struct ieee80211_sta_vht_cap *vht_cap,
struct iwl_tlc_config_cmd *cmd)
{
u16 supp;
int i, highest_mcs;
for (i = 0; i < sta->rx_nss; i++) {
if (i == MAX_RS_ANT_NUM)
break;
highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, i + 1);
if (!highest_mcs)
continue;
supp = BIT(highest_mcs + 1) - 1;
if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
cmd->ht_supp_rates[i] = cpu_to_le16(supp);
}
}
static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband,
struct iwl_tlc_config_cmd *cmd)
{
int i;
unsigned long tmp;
unsigned long supp; /* must be unsigned long for for_each_set_bit */
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
/* non HT rates */
supp = 0;
tmp = sta->supp_rates[sband->band];
for_each_set_bit(i, &tmp, BITS_PER_LONG)
supp |= BIT(sband->bitrates[i].hw_value);
cmd->non_ht_supp_rates = cpu_to_le16(supp);
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
/* HT/VHT rates */
if (vht_cap && vht_cap->vht_supported) {
cmd->mode = IWL_TLC_MNG_MODE_VHT;
rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd);
} else if (ht_cap && ht_cap->ht_supported) {
cmd->mode = IWL_TLC_MNG_MODE_HT;
cmd->ht_supp_rates[0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]);
cmd->ht_supp_rates[1] = cpu_to_le16(ht_cap->mcs.rx_mask[1]);
}
}
static void rs_fw_tlc_mng_notif_req_config(struct iwl_mvm *mvm, u8 sta_id)
{
u32 cmd_id = iwl_cmd_id(TLC_MNG_NOTIF_REQ_CMD, DATA_PATH_GROUP, 0);
struct iwl_tlc_notif_req_config_cmd cfg_cmd = {
.sta_id = sta_id,
.flags = cpu_to_le16(IWL_TLC_NOTIF_INIT_RATE_MSK),
.interval = cpu_to_le16(IWL_TLC_NOTIF_REQ_INTERVAL),
};
int ret;
ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd);
if (ret)
IWL_ERR(mvm, "Failed to send TLC notif request (%d)\n", ret);
}
void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
{
struct iwl_tlc_update_notif *notif;
struct iwl_mvm_sta *mvmsta;
struct iwl_lq_sta_rs_fw *lq_sta;
notif = (void *)pkt->data;
mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, notif->sta_id);
if (!mvmsta) {
IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n",
notif->sta_id);
return;
}
lq_sta = &mvmsta->lq_sta.rs_fw;
if (le16_to_cpu(notif->flags) & IWL_TLC_NOTIF_INIT_RATE_MSK) {
lq_sta->last_rate_n_flags =
le32_to_cpu(notif->values[IWL_TLC_NOTIF_INIT_RATE_POS]);
IWL_DEBUG_RATE(mvm, "new rate_n_flags: 0x%X\n",
lq_sta->last_rate_n_flags);
}
}
void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band)
{
struct ieee80211_hw *hw = mvm->hw;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
u32 cmd_id = iwl_cmd_id(TLC_MNG_CONFIG_CMD, DATA_PATH_GROUP, 0);
struct ieee80211_supported_band *sband;
struct iwl_tlc_config_cmd cfg_cmd = {
.sta_id = mvmsta->sta_id,
.max_supp_ch_width = rs_fw_bw_from_sta_bw(sta),
.flags = cpu_to_le16(rs_fw_set_config_flags(mvm, sta)),
.chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)),
.max_supp_ss = sta->rx_nss,
.max_ampdu_cnt = cpu_to_le32(mvmsta->max_agg_bufsize),
.sgi_ch_width_supp = rs_fw_sgi_cw_support(sta),
};
int ret;
memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
#ifdef CONFIG_IWLWIFI_DEBUGFS
iwl_mvm_reset_frame_stats(mvm);
#endif
sband = hw->wiphy->bands[band];
rs_fw_set_supp_rates(sta, sband, &cfg_cmd);
ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd);
if (ret)
IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret);
rs_fw_tlc_mng_notif_req_config(mvm, cfg_cmd.sta_id);
}
int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
{
/* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */
IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n");
return 0;
}
void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta)
{
struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
lq_sta->pers.drv = mvm;
lq_sta->pers.sta_id = mvmsta->sta_id;
lq_sta->pers.chains = 0;
memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal));
lq_sta->pers.last_rssi = S8_MIN;
lq_sta->last_rate_n_flags = 0;
#ifdef CONFIG_MAC80211_DEBUGFS
lq_sta->pers.dbg_fixed_rate = 0;
#endif
}

View file

@ -42,8 +42,6 @@
#include "mvm.h"
#include "debugfs.h"
#define RS_NAME "iwl-mvm-rs"
#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */
/* Calculations of success ratio are done in fixed point where 12800 is 100%.
@ -809,7 +807,7 @@ static int rs_collect_tlc_data(struct iwl_mvm *mvm,
return -EINVAL;
if (tbl->column != RS_COLUMN_INVALID) {
struct lq_sta_pers *pers = &mvmsta->lq_sta.pers;
struct lq_sta_pers *pers = &mvmsta->lq_sta.rs_drv.pers;
pers->tx_stats[tbl->column][scale_index].total += attempts;
pers->tx_stats[tbl->column][scale_index].success += successes;
@ -1206,7 +1204,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info);
u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta;
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
/* Treat uninitialized rate scaling data same as non-existing. */
if (!lq_sta) {
@ -1416,13 +1414,13 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
/*
* mac80211 sends us Tx status
*/
static void rs_mac80211_tx_status(void *mvm_r,
struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
static void rs_drv_mac80211_tx_status(void *mvm_r,
struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r;
struct iwl_op_mode *op_mode = mvm_r;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@ -1877,12 +1875,10 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
struct rs_rate *rate = &search_tbl->rate;
const struct rs_tx_column *column = &rs_tx_columns[col_id];
const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
unsigned long rate_mask = 0;
u32 rate_idx = 0;
memcpy(search_tbl, tbl, sz);
memcpy(search_tbl, tbl, offsetof(struct iwl_scale_tbl_info, win));
rate->sgi = column->sgi;
rate->ant = column->ant;
@ -2787,9 +2783,10 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm,
/* Save info about RSSI of last Rx */
void rs_update_last_rssi(struct iwl_mvm *mvm,
struct iwl_lq_sta *lq_sta,
struct iwl_mvm_sta *mvmsta,
struct ieee80211_rx_status *rx_status)
{
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
int i;
lq_sta->pers.chains = rx_status->chains;
@ -2858,15 +2855,15 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
}
static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
struct ieee80211_tx_rate_control *txrc)
static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
void *mvm_sta,
struct ieee80211_tx_rate_control *txrc)
{
struct sk_buff *skb = txrc->skb;
struct iwl_op_mode *op_mode __maybe_unused =
(struct iwl_op_mode *)mvm_r;
struct iwl_op_mode *op_mode = mvm_r;
struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
struct sk_buff *skb = txrc->skb;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_lq_sta *lq_sta = mvm_sta;
struct iwl_lq_sta *lq_sta;
struct rs_rate *optimal_rate;
u32 last_ucode_rate;
@ -2878,18 +2875,14 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
mvm_sta = NULL;
}
/* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
/* Treat uninitialized rate scaling data same as non-existing. */
if (lq_sta && !lq_sta->pers.drv) {
IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
mvm_sta = NULL;
}
/* Send management frames and NO_ACK data using lowest rate. */
if (rate_control_send_low(sta, mvm_sta, txrc))
return;
if (!mvm_sta)
return;
lq_sta = mvm_sta;
iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags,
info->band, &info->control.rates[0]);
info->control.rates[0].count = 1;
@ -2906,13 +2899,13 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
}
}
static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
gfp_t gfp)
static void *rs_drv_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
gfp_t gfp)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta;
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
@ -2926,7 +2919,7 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal));
lq_sta->pers.last_rssi = S8_MIN;
return &mvmsta->lq_sta;
return lq_sta;
}
static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
@ -3043,7 +3036,7 @@ static void rs_vht_init(struct iwl_mvm *mvm,
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm)
void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm)
{
spin_lock_bh(&mvm->drv_stats_lock);
memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats));
@ -3111,15 +3104,15 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
/*
* Called after adding a new station to initialize rate scaling
*/
void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band, bool init)
static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band, bool init)
{
int i, j;
struct ieee80211_hw *hw = mvm->hw;
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta;
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
struct ieee80211_supported_band *sband;
unsigned long supp; /* must be unsigned long for for_each_set_bit */
@ -3194,16 +3187,15 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
rs_initialize_lq(mvm, sta, lq_sta, band, init);
}
static void rs_rate_update(void *mvm_r,
struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta,
u32 changed)
static void rs_drv_rate_update(void *mvm_r,
struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta,
void *priv_sta, u32 changed)
{
struct iwl_op_mode *op_mode = mvm_r;
struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
u8 tid;
struct iwl_op_mode *op_mode =
(struct iwl_op_mode *)mvm_r;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
if (!iwl_mvm_sta_from_mac80211(sta)->vif)
return;
@ -3385,7 +3377,7 @@ static void rs_bfer_active_iter(void *_data,
{
struct rs_bfer_active_iter_data *data = _data;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq;
struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.rs_drv.lq;
u32 ss_params = le32_to_cpu(lq_cmd->ss_params);
if (sta == data->exclude_sta)
@ -3497,7 +3489,8 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
/* Disallow BFER on another STA if active and we're a higher priority */
if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) {
struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq;
struct iwl_lq_cmd *bfersta_lq_cmd =
&bfer_mvmsta->lq_sta.rs_drv.lq;
u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params);
bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED;
@ -3569,14 +3562,14 @@ static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
{
return hw->priv;
}
/* rate scale requires free function to be implemented */
static void rs_free(void *mvm_rate)
{
return;
}
static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
void *mvm_sta)
static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta)
{
struct iwl_op_mode *op_mode __maybe_unused = mvm_r;
struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
@ -3586,7 +3579,7 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
}
#ifdef CONFIG_MAC80211_DEBUGFS
int rs_pretty_print_rate(char *buf, const u32 rate)
int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
{
char *type, *bw;
@ -3597,10 +3590,10 @@ int rs_pretty_print_rate(char *buf, const u32 rate)
!(rate & RATE_MCS_VHT_MSK)) {
int index = iwl_hwrate_to_plcp_idx(rate);
return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n",
rs_pretty_ant(ant),
index == IWL_RATE_INVALID ? "BAD" :
iwl_rate_mcs[index].mbps);
return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps\n",
rs_pretty_ant(ant),
index == IWL_RATE_INVALID ? "BAD" :
iwl_rate_mcs[index].mbps);
}
if (rate & RATE_MCS_VHT_MSK) {
@ -3634,12 +3627,13 @@ int rs_pretty_print_rate(char *buf, const u32 rate)
bw = "BAD BW";
}
return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s\n",
type, rs_pretty_ant(ant), bw, mcs, nss,
(rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ",
(rate & RATE_MCS_STBC_MSK) ? "STBC " : "",
(rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
(rate & RATE_MCS_BF_MSK) ? "BF " : "");
return scnprintf(buf, bufsz,
"%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s\n",
type, rs_pretty_ant(ant), bw, mcs, nss,
(rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ",
(rate & RATE_MCS_STBC_MSK) ? "STBC " : "",
(rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
(rate & RATE_MCS_BF_MSK) ? "BF " : "");
}
/**
@ -3696,65 +3690,70 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
int desc = 0;
int i = 0;
ssize_t ret;
static const size_t bufsz = 2048;
struct iwl_lq_sta *lq_sta = file->private_data;
struct iwl_mvm_sta *mvmsta =
container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
container_of(lq_sta, struct iwl_mvm_sta, lq_sta.rs_drv);
struct iwl_mvm *mvm;
struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
struct rs_rate *rate = &tbl->rate;
u32 ss_params;
mvm = lq_sta->pers.drv;
buff = kmalloc(2048, GFP_KERNEL);
buff = kmalloc(bufsz, GFP_KERNEL);
if (!buff)
return -ENOMEM;
desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n",
lq_sta->total_failed, lq_sta->total_success,
lq_sta->active_legacy_rate);
desc += sprintf(buff+desc, "fixed rate 0x%X\n",
lq_sta->pers.dbg_fixed_rate);
desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
desc += scnprintf(buff + desc, bufsz - desc,
"sta_id %d\n", lq_sta->lq.sta_id);
desc += scnprintf(buff + desc, bufsz - desc,
"failed=%d success=%d rate=0%lX\n",
lq_sta->total_failed, lq_sta->total_success,
lq_sta->active_legacy_rate);
desc += scnprintf(buff + desc, bufsz - desc, "fixed rate 0x%X\n",
lq_sta->pers.dbg_fixed_rate);
desc += scnprintf(buff + desc, bufsz - desc, "valid_tx_ant %s%s%s\n",
(iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",
(iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "",
(iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : "");
desc += sprintf(buff+desc, "lq type %s\n",
(is_legacy(rate)) ? "legacy" :
is_vht(rate) ? "VHT" : "HT");
desc += scnprintf(buff + desc, bufsz - desc, "lq type %s\n",
(is_legacy(rate)) ? "legacy" :
is_vht(rate) ? "VHT" : "HT");
if (!is_legacy(rate)) {
desc += sprintf(buff + desc, " %s",
desc += scnprintf(buff + desc, bufsz - desc, " %s",
(is_siso(rate)) ? "SISO" : "MIMO2");
desc += sprintf(buff + desc, " %s",
desc += scnprintf(buff + desc, bufsz - desc, " %s",
(is_ht20(rate)) ? "20MHz" :
(is_ht40(rate)) ? "40MHz" :
(is_ht80(rate)) ? "80MHz" :
(is_ht160(rate)) ? "160MHz" : "BAD BW");
desc += sprintf(buff + desc, " %s %s %s %s\n",
desc += scnprintf(buff + desc, bufsz - desc, " %s %s %s %s\n",
(rate->sgi) ? "SGI" : "NGI",
(rate->ldpc) ? "LDPC" : "BCC",
(lq_sta->is_agg) ? "AGG on" : "",
(mvmsta->tlc_amsdu) ? "AMSDU on" : "");
}
desc += sprintf(buff+desc, "last tx rate=0x%X\n",
desc += scnprintf(buff + desc, bufsz - desc, "last tx rate=0x%X\n",
lq_sta->last_rate_n_flags);
desc += sprintf(buff+desc,
desc += scnprintf(buff + desc, bufsz - desc,
"general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n",
lq_sta->lq.flags,
lq_sta->lq.mimo_delim,
lq_sta->lq.single_stream_ant_msk,
lq_sta->lq.dual_stream_ant_msk);
desc += sprintf(buff+desc,
desc += scnprintf(buff + desc, bufsz - desc,
"agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
le16_to_cpu(lq_sta->lq.agg_time_limit),
lq_sta->lq.agg_disable_start_th,
lq_sta->lq.agg_frame_cnt_limit);
desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
desc += scnprintf(buff + desc, bufsz - desc, "reduced tpc=%d\n",
lq_sta->lq.reduced_tpc);
ss_params = le32_to_cpu(lq_sta->lq.ss_params);
desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n",
desc += scnprintf(buff + desc, bufsz - desc,
"single stream params: %s%s%s%s\n",
(ss_params & LQ_SS_PARAMS_VALID) ?
"VALID" : "INVALID",
(ss_params & LQ_SS_BFER_ALLOWED) ?
@ -3763,7 +3762,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
", STBC" : "",
(ss_params & LQ_SS_FORCE) ?
", FORCE" : "");
desc += sprintf(buff+desc,
desc += scnprintf(buff + desc, bufsz - desc,
"Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
lq_sta->lq.initial_rate_index[0],
lq_sta->lq.initial_rate_index[1],
@ -3773,8 +3772,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]);
desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r);
desc += rs_pretty_print_rate(buff+desc, r);
desc += scnprintf(buff + desc, bufsz - desc,
" rate[%d] 0x%X ", i, r);
desc += rs_pretty_print_rate(buff + desc, bufsz - desc, r);
}
ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
@ -3987,12 +3987,13 @@ static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf,
MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32);
static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
static void rs_drv_add_sta_debugfs(void *mvm, void *priv_sta,
struct dentry *dir)
{
struct iwl_lq_sta *lq_sta = priv_sta;
struct iwl_mvm_sta *mvmsta;
mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta.rs_drv);
if (!mvmsta->vif)
return;
@ -4014,7 +4015,7 @@ static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n");
}
static void rs_remove_debugfs(void *mvm, void *mvm_sta)
void rs_remove_sta_debugfs(void *mvm, void *mvm_sta)
{
}
#endif
@ -4024,50 +4025,53 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
* the station is added. Since mac80211 calls this function before a
* station is added we ignore it.
*/
static void rs_rate_init_stub(void *mvm_r,
struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *mvm_sta)
static void rs_rate_init_ops(void *mvm_r,
struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *mvm_sta)
{
}
static const struct rate_control_ops rs_mvm_ops = {
/* ops for rate scaling implemented in the driver */
static const struct rate_control_ops rs_mvm_ops_drv = {
.name = RS_NAME,
.tx_status = rs_mac80211_tx_status,
.get_rate = rs_get_rate,
.rate_init = rs_rate_init_stub,
.tx_status = rs_drv_mac80211_tx_status,
.get_rate = rs_drv_get_rate,
.rate_init = rs_rate_init_ops,
.alloc = rs_alloc,
.free = rs_free,
.alloc_sta = rs_alloc_sta,
.alloc_sta = rs_drv_alloc_sta,
.free_sta = rs_free_sta,
.rate_update = rs_rate_update,
.rate_update = rs_drv_rate_update,
#ifdef CONFIG_MAC80211_DEBUGFS
.add_sta_debugfs = rs_add_debugfs,
.remove_sta_debugfs = rs_remove_debugfs,
.add_sta_debugfs = rs_drv_add_sta_debugfs,
.remove_sta_debugfs = rs_remove_sta_debugfs,
#endif
};
void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band, bool init)
{
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD))
rs_fw_rate_init(mvm, sta, band);
else
rs_drv_rate_init(mvm, sta, band, init);
}
int iwl_mvm_rate_control_register(void)
{
return ieee80211_rate_control_register(&rs_mvm_ops);
return ieee80211_rate_control_register(&rs_mvm_ops_drv);
}
void iwl_mvm_rate_control_unregister(void)
{
ieee80211_rate_control_unregister(&rs_mvm_ops);
ieee80211_rate_control_unregister(&rs_mvm_ops_drv);
}
/**
* iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
* Tx protection, according to this request and previous requests,
* and send the LQ command.
* @mvmsta: The station
* @enable: Enable Tx protection?
*/
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
{
struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq;
struct iwl_lq_cmd *lq = &mvmsta->lq_sta.rs_drv.lq;
lockdep_assert_held(&mvm->mutex);
@ -4083,3 +4087,17 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
return iwl_mvm_send_lq_cmd(mvm, lq, false);
}
/**
* iwl_mvm_tx_protection - ask FW to enable RTS/CTS protection
* @mvmsta: The station
* @enable: Enable Tx protection?
*/
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
{
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD))
return rs_fw_tx_protection(mvm, mvmsta, enable);
else
return rs_drv_tx_protection(mvm, mvmsta, enable);
}

View file

@ -36,6 +36,8 @@
#include "fw-api.h"
#include "iwl-trans.h"
#define RS_NAME "iwl-mvm-rs"
struct iwl_rs_rate_info {
u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */
u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */
@ -217,6 +219,38 @@ struct iwl_rate_mcs_info {
char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
};
/**
* struct iwl_lq_sta_rs_fw - rate and related statistics for RS in FW
* @last_rate_n_flags: last rate reported by FW
* @sta_id: the id of the station
#ifdef CONFIG_MAC80211_DEBUGFS
* @dbg_fixed_rate: for debug, use fixed rate if not 0
* @dbg_agg_frame_count_lim: for debug, max number of frames in A-MPDU
#endif
* @chains: bitmask of chains reported in %chain_signal
* @chain_signal: per chain signal strength
* @last_rssi: last rssi reported
* @drv: pointer back to the driver data
*/
struct iwl_lq_sta_rs_fw {
/* last tx rate_n_flags */
u32 last_rate_n_flags;
/* persistent fields - initialized only once - keep last! */
struct lq_sta_pers_rs_fw {
u32 sta_id;
#ifdef CONFIG_MAC80211_DEBUGFS
u32 dbg_fixed_rate;
u16 dbg_agg_frame_count_lim;
#endif
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
s8 last_rssi;
struct iwl_mvm *drv;
} pers;
};
/**
* struct iwl_rate_scale_data -- tx success history for one rate
*/
@ -407,4 +441,18 @@ struct iwl_mvm_sta;
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable);
#ifdef CONFIG_IWLWIFI_DEBUGFS
void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm);
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
void rs_remove_sta_debugfs(void *mvm, void *mvm_sta);
#endif
void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta);
void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band);
int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable);
void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt);
#endif /* __rs__ */

View file

@ -383,7 +383,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
false);
}
rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
rs_update_last_rssi(mvm, mvmsta, rx_status);
if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
ieee80211_is_beacon(hdr->frame_control)) {
@ -439,7 +439,8 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
rx_status->bw = RATE_INFO_BW_160;
break;
}
if (rate_n_flags & RATE_MCS_SGI_MSK)
if (!(rate_n_flags & RATE_MCS_CCK_MSK) &&
rate_n_flags & RATE_MCS_SGI_MSK)
rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
if (rate_n_flags & RATE_HT_MCS_GF_MSK)
rx_status->enc_flags |= RX_ENC_FLAG_HT_GF;

View file

@ -943,7 +943,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
false);
}
rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
rs_update_last_rssi(mvm, mvmsta, rx_status);
if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
ieee80211_is_beacon(hdr->frame_control)) {
@ -1020,7 +1020,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
rx_status->bw = RATE_INFO_BW_160;
break;
}
if (rate_n_flags & RATE_MCS_SGI_MSK)
if (!(rate_n_flags & RATE_MCS_CCK_MSK) &&
rate_n_flags & RATE_MCS_SGI_MSK)
rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
if (rate_n_flags & RATE_HT_MCS_GF_MSK)
rx_status->enc_flags |= RX_ENC_FLAG_HT_GF;

View file

@ -664,6 +664,22 @@ static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
return newpos;
}
#define WFA_TPC_IE_LEN 9
static void iwl_mvm_add_tpc_report_ie(u8 *pos)
{
pos[0] = WLAN_EID_VENDOR_SPECIFIC;
pos[1] = WFA_TPC_IE_LEN - 2;
pos[2] = (WLAN_OUI_MICROSOFT >> 16) & 0xff;
pos[3] = (WLAN_OUI_MICROSOFT >> 8) & 0xff;
pos[4] = WLAN_OUI_MICROSOFT & 0xff;
pos[5] = WLAN_OUI_TYPE_MICROSOFT_TPC;
pos[6] = 0;
/* pos[7] - tx power will be inserted by the FW */
pos[7] = 0;
pos[8] = 0;
}
static void
iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_scan_ies *ies,
@ -716,7 +732,16 @@ iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
memcpy(pos, ies->common_ies, ies->common_ie_len);
params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf);
params->preq.common_data.len = cpu_to_le16(ies->common_ie_len);
if (iwl_mvm_rrm_scan_needed(mvm) &&
!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT)) {
iwl_mvm_add_tpc_report_ie(pos + ies->common_ie_len);
params->preq.common_data.len = cpu_to_le16(ies->common_ie_len +
WFA_TPC_IE_LEN);
} else {
params->preq.common_data.len = cpu_to_le16(ies->common_ie_len);
}
}
static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm,
@ -781,7 +806,9 @@ static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm,
if (params->type == IWL_SCAN_TYPE_FRAGMENTED)
flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
if (iwl_mvm_rrm_scan_needed(mvm))
if (iwl_mvm_rrm_scan_needed(mvm) &&
fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT))
flags |= IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED;
if (params->pass_all)
@ -1183,7 +1210,9 @@ static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
flags |= IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED;
}
if (iwl_mvm_rrm_scan_needed(mvm))
if (iwl_mvm_rrm_scan_needed(mvm) &&
fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT))
flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
if (params->pass_all)

View file

@ -1439,6 +1439,13 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
goto err;
}
/*
* if rs is registered with mac80211, then "add station" will be handled
* via the corresponding ops, otherwise need to notify rate scaling here
*/
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD))
iwl_mvm_rs_add_sta(mvm, mvm_sta);
update_fw:
ret = iwl_mvm_sta_send_to_fw(mvm, sta, sta_update, sta_flags);
if (ret)
@ -1762,7 +1769,7 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
}
/*
* For a000 firmware and on we cannot add queue to a station unknown
* For 22000 firmware and on we cannot add queue to a station unknown
* to firmware so enable queue here - after the station was added
*/
if (iwl_mvm_has_new_tx_api(mvm))
@ -1885,7 +1892,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
return ret;
/*
* For a000 firmware and on we cannot add queue to a station unknown
* For 22000 firmware and on we cannot add queue to a station unknown
* to firmware so enable queue here - after the station was added
*/
if (iwl_mvm_has_new_tx_api(mvm)) {
@ -2064,7 +2071,7 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
/*
* Enable cab queue after the ADD_STA command is sent.
* This is needed for a000 firmware which won't accept SCD_QUEUE_CFG
* This is needed for 22000 firmware which won't accept SCD_QUEUE_CFG
* command with unknown station id, and for FW that doesn't support
* station API since the cab queue is not included in the
* tfd_queue_mask.
@ -2530,7 +2537,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
tid_data->next_reclaimed);
/*
* In A000 HW, the next_reclaimed index is only 8 bit, so we'll need
* In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need
* to align the wrap around of ssn so we compare relevant values.
*/
normalized_ssn = tid_data->ssn;
@ -2575,6 +2582,14 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
.aggregate = true,
};
/*
* When FW supports TLC_OFFLOAD, it also implements Tx aggregation
* manager, so this function should never be called in this case.
*/
if (WARN_ON_ONCE(fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_TLC_OFFLOAD)))
return -EINVAL;
BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE)
!= IWL_MAX_TID_COUNT);
@ -2672,12 +2687,12 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
*/
mvmsta->max_agg_bufsize =
min(mvmsta->max_agg_bufsize, buf_size);
mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
mvmsta->lq_sta.rs_drv.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
sta->addr, tid);
return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);
return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq, false);
}
static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
@ -3615,7 +3630,7 @@ u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data)
u16 sn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
/*
* In A000 HW, the next_reclaimed index is only 8 bit, so we'll need
* In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need
* to align the wrap around of ssn so we compare relevant values.
*/
if (mvm->trans->cfg->gen2)

View file

@ -383,6 +383,8 @@ struct iwl_mvm_rxq_dup_data {
* and from Tx response flow, it needs a spinlock.
* @tid_data: per tid data + mgmt. Look at %iwl_mvm_tid_data.
* @tid_to_baid: a simple map of TID to baid
* @lq_sta: holds rate scaling data, either for the case when RS is done in
* the driver - %rs_drv or in the FW - %rs_fw.
* @reserved_queue: the queue reserved for this STA for DQA purposes
* Every STA has is given one reserved queue to allow it to operate. If no
* such queue can be guaranteed, the STA addition will fail.
@ -417,7 +419,10 @@ struct iwl_mvm_sta {
spinlock_t lock;
struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT + 1];
u8 tid_to_baid[IWL_MAX_TID_COUNT];
struct iwl_lq_sta lq_sta;
union {
struct iwl_lq_sta_rs_fw rs_fw;
struct iwl_lq_sta rs_drv;
} lq_sta;
struct ieee80211_vif *vif;
struct iwl_mvm_key_pn __rcu *ptk_pn[4];
struct iwl_mvm_rxq_dup_data *dup_data;

View file

@ -101,7 +101,6 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
void iwl_mvm_roc_done_wk(struct work_struct *wk)
{
struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
u32 queues = 0;
/*
* Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit.
@ -110,14 +109,10 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
* in the case that the time event actually completed in the firmware
* (which is handled in iwl_mvm_te_handle_notif).
*/
if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) {
queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE);
if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
}
if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) {
queues |= BIT(mvm->aux_queue);
if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX);
}
synchronize_net();
@ -777,12 +772,6 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return -EBUSY;
}
/*
* Flush the done work, just in case it's still pending, so that
* the work it does can complete and we can accept new frames.
*/
flush_work(&mvm->roc_done_wk);
time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
time_cmd.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));

View file

@ -1132,7 +1132,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
}
/*
* In A000 HW, the next_reclaimed index is only 8 bit, so we'll need
* In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need
* to align the wrap around of ssn so we compare relevant values.
*/
normalized_ssn = tid_data->ssn;
@ -1624,7 +1624,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
int freed;
if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT ||
tid >= IWL_MAX_TID_COUNT,
tid > IWL_MAX_TID_COUNT,
"sta_id %d tid %d", sta_id, tid))
return;
@ -1679,7 +1679,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
if (ieee80211_is_data_qos(hdr->frame_control))
freed++;
else
WARN_ON_ONCE(1);
WARN_ON_ONCE(tid != IWL_MAX_TID_COUNT);
iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
@ -1719,8 +1719,12 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
ba_info->band = chanctx_conf->def.chan->band;
iwl_mvm_hwrate_to_tx_status(rate, ba_info);
IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n");
iwl_mvm_rs_tx_status(mvm, sta, tid, ba_info, false);
if (!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_TLC_OFFLOAD)) {
IWL_DEBUG_TX_REPLY(mvm,
"No reclaim. Update rs directly\n");
iwl_mvm_rs_tx_status(mvm, sta, tid, ba_info, false);
}
}
out:
@ -1771,8 +1775,12 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
struct iwl_mvm_compressed_ba_tfd *ba_tfd =
&ba_res->tfd[i];
tid = ba_tfd->tid;
if (tid == IWL_MGMT_TID)
tid = IWL_MAX_TID_COUNT;
mvmsta->tid_data[i].lq_color = lq_color;
iwl_mvm_tx_reclaim(mvm, sta_id, ba_tfd->tid,
iwl_mvm_tx_reclaim(mvm, sta_id, tid,
(int)(le16_to_cpu(ba_tfd->q_num)),
le16_to_cpu(ba_tfd->tfd_index),
&ba_info,

View file

@ -278,8 +278,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
u8 ind = last_idx;
int i;
for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
ind = (ind + 1) % RATE_MCS_ANT_NUM;
for (i = 0; i < MAX_RS_ANT_NUM; i++) {
ind = (ind + 1) % MAX_RS_ANT_NUM;
if (valid & BIT(ind))
return ind;
}
@ -595,6 +595,12 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base)
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
{
if (!test_bit(STATUS_DEVICE_ENABLED, &mvm->trans->status)) {
IWL_ERR(mvm,
"DEVICE_ENABLED bit is not set. Aborting dump.\n");
return;
}
iwl_mvm_dump_lmac_error_log(mvm, mvm->error_event_table[0]);
if (mvm->error_event_table[1])
@ -906,7 +912,9 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)
.data = { lq, },
};
if (WARN_ON(lq->sta_id == IWL_MVM_INVALID_STA))
if (WARN_ON(lq->sta_id == IWL_MVM_INVALID_STA ||
fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_TLC_OFFLOAD)))
return -EINVAL;
return iwl_mvm_send_cmd(mvm, &cmd);

View file

@ -652,20 +652,20 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0xA370, 0x4034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x40A4, iwl9462_2ac_cfg_soc)},
/* a000 Series */
{IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x34F0, 0x0310, iwla000_2ac_cfg_jf)},
{IWL_PCI_DEVICE(0x2720, 0x0000, iwla000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0070, iwla000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x2720, 0x0078, iwla000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x2720, 0x0070, iwla000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x2720, 0x0030, iwla000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x2720, 0x1080, iwla000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x2720, 0x0090, iwla000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x2720, 0x0310, iwla000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x40C0, 0x0000, iwla000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x40C0, 0x0A10, iwla000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0xA0F0, 0x0000, iwla000_2ax_cfg_hr)},
/* 22000 Series */
{IWL_PCI_DEVICE(0x2720, 0x0A10, iwl22000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ac_cfg_jf)},
{IWL_PCI_DEVICE(0x2720, 0x0000, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x2720, 0x0078, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x2720, 0x0070, iwl22000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x2720, 0x0030, iwl22000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x2720, 0x1080, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x2720, 0x0090, iwl22000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x2720, 0x0310, iwl22000_2ac_cfg_hr_cdb)},
{IWL_PCI_DEVICE(0x40C0, 0x0000, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x40C0, 0x0A10, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0xA0F0, 0x0000, iwl22000_2ax_cfg_hr)},
#endif /* CONFIG_IWLMVM */
@ -707,7 +707,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
iwl_trans->cfg = cfg_7265d;
}
if (iwl_trans->cfg->rf_id && cfg == &iwla000_2ac_cfg_hr_cdb &&
if (iwl_trans->cfg->rf_id && cfg == &iwl22000_2ac_cfg_hr_cdb &&
iwl_trans->hw_rev != CSR_HW_REV_TYPE_HR_CDB) {
u32 rf_id_chp = CSR_HW_RF_ID_TYPE_CHIP_ID(iwl_trans->hw_rf_id);
u32 jf_chp_id = CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_JF);
@ -715,14 +715,14 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rf_id_chp == jf_chp_id) {
if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QNJ)
cfg = &iwla000_2ax_cfg_qnj_jf_b0;
cfg = &iwl22000_2ax_cfg_qnj_jf_b0;
else
cfg = &iwla000_2ac_cfg_jf;
cfg = &iwl22000_2ac_cfg_jf;
} else if (rf_id_chp == hr_chp_id) {
if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QNJ)
cfg = &iwla000_2ax_cfg_qnj_hr_a0;
cfg = &iwl22000_2ax_cfg_qnj_hr_a0;
else
cfg = &iwla000_2ac_cfg_hr;
cfg = &iwl22000_2ac_cfg_hr;
}
iwl_trans->cfg = cfg;
}

View file

@ -3250,9 +3250,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS);
if (hw_status & UMAG_GEN_HW_IS_FPGA)
trans->cfg = &iwla000_2ax_cfg_qnj_hr_f0;
trans->cfg = &iwl22000_2ax_cfg_qnj_hr_f0;
else
trans->cfg = &iwla000_2ac_cfg_hr;
trans->cfg = &iwl22000_2ac_cfg_hr;
}
#endif

View file

@ -1116,6 +1116,12 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
if (priv->scan_request) {
mwifiex_dbg(priv->adapter, ERROR,
"change virtual interface: scan in process\n");
return -EBUSY;
}
switch (curr_iftype) {
case NL80211_IFTYPE_ADHOC:
switch (type) {
@ -1180,7 +1186,6 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
case NL80211_IFTYPE_AP:
switch (type) {
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_STATION:
return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype,
type, params);
break;

View file

@ -199,7 +199,7 @@ struct mwl8k_priv {
struct ieee80211_channel channels_24[14];
struct ieee80211_rate rates_24[13];
struct ieee80211_supported_band band_50;
struct ieee80211_channel channels_50[4];
struct ieee80211_channel channels_50[9];
struct ieee80211_rate rates_50[8];
u32 ap_macids_supported;
u32 sta_macids_supported;
@ -383,6 +383,11 @@ static const struct ieee80211_channel mwl8k_channels_50[] = {
{ .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
{ .band = NL80211_BAND_5GHZ, .center_freq = 5825, .hw_value = 165, },
};
static const struct ieee80211_rate mwl8k_rates_50[] = {

View file

@ -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

View file

@ -1 +1,2 @@
obj-$(CONFIG_MT7601U) += mt7601u/
obj-$(CONFIG_MT76_CORE) += mt76/

View file

@ -0,0 +1,10 @@
config MT76_CORE
tristate
config MT76x2E
tristate "MediaTek MT76x2E (PCIe) support"
select MT76_CORE
depends on MAC80211
depends on PCI
---help---
This adds support for MT7612/MT7602/MT7662-based wireless PCIe devices.

View file

@ -0,0 +1,15 @@
obj-$(CONFIG_MT76_CORE) += mt76.o
obj-$(CONFIG_MT76x2E) += mt76x2e.o
mt76-y := \
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o
CFLAGS_trace.o := -I$(src)
mt76x2e-y := \
mt76x2_pci.o mt76x2_dma.o \
mt76x2_main.o mt76x2_init.o mt76x2_debugfs.o mt76x2_tx.o \
mt76x2_core.o mt76x2_mac.o mt76x2_eeprom.o mt76x2_mcu.o mt76x2_phy.o \
mt76x2_dfs.o mt76x2_trace.o
CFLAGS_mt76x2_trace.o := -I$(src)

View file

@ -0,0 +1,77 @@
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "mt76.h"
static int
mt76_reg_set(void *data, u64 val)
{
struct mt76_dev *dev = data;
dev->bus->wr(dev, dev->debugfs_reg, val);
return 0;
}
static int
mt76_reg_get(void *data, u64 *val)
{
struct mt76_dev *dev = data;
*val = dev->bus->rr(dev, dev->debugfs_reg);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, "0x%08llx\n");
static int
mt76_queues_read(struct seq_file *s, void *data)
{
struct mt76_dev *dev = dev_get_drvdata(s->private);
int i;
for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) {
struct mt76_queue *q = &dev->q_tx[i];
if (!q->ndesc)
continue;
seq_printf(s,
"%d: queued=%d head=%d tail=%d swq_queued=%d\n",
i, q->queued, q->head, q->tail, q->swq_queued);
}
return 0;
}
struct dentry *mt76_register_debugfs(struct mt76_dev *dev)
{
struct dentry *dir;
dir = debugfs_create_dir("mt76", dev->hw->wiphy->debugfsdir);
if (!dir)
return NULL;
debugfs_create_u8("led_pin", S_IRUSR | S_IWUSR, dir, &dev->led_pin);
debugfs_create_u32("regidx", S_IRUSR | S_IWUSR, dir, &dev->debugfs_reg);
debugfs_create_file("regval", S_IRUSR | S_IWUSR, dir, dev,
&fops_regval);
debugfs_create_blob("eeprom", S_IRUSR, dir, &dev->eeprom);
if (dev->otp.data)
debugfs_create_blob("otp", S_IRUSR, dir, &dev->otp);
debugfs_create_devm_seqfile(dev->dev, "queues", dir, mt76_queues_read);
return dir;
}
EXPORT_SYMBOL_GPL(mt76_register_debugfs);

View file

@ -0,0 +1,451 @@
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/dma-mapping.h>
#include "mt76.h"
#include "dma.h"
#define DMA_DUMMY_TXWI ((void *) ~0)
static int
mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
int size;
int i;
spin_lock_init(&q->lock);
INIT_LIST_HEAD(&q->swq);
size = q->ndesc * sizeof(struct mt76_desc);
q->desc = dmam_alloc_coherent(dev->dev, size, &q->desc_dma, GFP_KERNEL);
if (!q->desc)
return -ENOMEM;
size = q->ndesc * sizeof(*q->entry);
q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
if (!q->entry)
return -ENOMEM;
/* clear descriptors */
for (i = 0; i < q->ndesc; i++)
q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
iowrite32(q->desc_dma, &q->regs->desc_base);
iowrite32(0, &q->regs->cpu_idx);
iowrite32(0, &q->regs->dma_idx);
iowrite32(q->ndesc, &q->regs->ring_size);
return 0;
}
static int
mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_buf *buf, int nbufs, u32 info,
struct sk_buff *skb, void *txwi)
{
struct mt76_desc *desc;
u32 ctrl;
int i, idx = -1;
if (txwi)
q->entry[q->head].txwi = DMA_DUMMY_TXWI;
for (i = 0; i < nbufs; i += 2, buf += 2) {
u32 buf0 = buf[0].addr, buf1 = 0;
ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
if (i < nbufs - 1) {
buf1 = buf[1].addr;
ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
}
if (i == nbufs - 1)
ctrl |= MT_DMA_CTL_LAST_SEC0;
else if (i == nbufs - 2)
ctrl |= MT_DMA_CTL_LAST_SEC1;
idx = q->head;
q->head = (q->head + 1) % q->ndesc;
desc = &q->desc[idx];
WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
WRITE_ONCE(desc->info, cpu_to_le32(info));
WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
q->queued++;
}
q->entry[idx].txwi = txwi;
q->entry[idx].skb = skb;
return idx;
}
static void
mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
struct mt76_queue_entry *prev_e)
{
struct mt76_queue_entry *e = &q->entry[idx];
__le32 __ctrl = READ_ONCE(q->desc[idx].ctrl);
u32 ctrl = le32_to_cpu(__ctrl);
if (!e->txwi || !e->skb) {
__le32 addr = READ_ONCE(q->desc[idx].buf0);
u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
DMA_TO_DEVICE);
}
if (!(ctrl & MT_DMA_CTL_LAST_SEC0)) {
__le32 addr = READ_ONCE(q->desc[idx].buf1);
u32 len = FIELD_GET(MT_DMA_CTL_SD_LEN1, ctrl);
dma_unmap_single(dev->dev, le32_to_cpu(addr), len,
DMA_TO_DEVICE);
}
if (e->txwi == DMA_DUMMY_TXWI)
e->txwi = NULL;
*prev_e = *e;
memset(e, 0, sizeof(*e));
}
static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
q->head = ioread32(&q->regs->dma_idx);
q->tail = q->head;
iowrite32(q->head, &q->regs->cpu_idx);
}
static void
mt76_dma_tx_cleanup(struct mt76_dev *dev, enum mt76_txq_id qid, bool flush)
{
struct mt76_queue *q = &dev->q_tx[qid];
struct mt76_queue_entry entry;
bool wake = false;
int last;
if (!q->ndesc)
return;
spin_lock_bh(&q->lock);
if (flush)
last = -1;
else
last = ioread32(&q->regs->dma_idx);
while (q->queued && q->tail != last) {
mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
if (entry.schedule)
q->swq_queued--;
if (entry.skb)
dev->drv->tx_complete_skb(dev, q, &entry, flush);
if (entry.txwi) {
mt76_put_txwi(dev, entry.txwi);
wake = true;
}
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
if (!flush && q->tail == last)
last = ioread32(&q->regs->dma_idx);
}
if (!flush)
mt76_txq_schedule(dev, q);
else
mt76_dma_sync_idx(dev, q);
wake = wake && qid < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
spin_unlock_bh(&q->lock);
if (wake)
ieee80211_wake_queue(dev->hw, qid);
}
static void *
mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
int *len, u32 *info, bool *more)
{
struct mt76_queue_entry *e = &q->entry[idx];
struct mt76_desc *desc = &q->desc[idx];
dma_addr_t buf_addr;
void *buf = e->buf;
int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
buf_addr = le32_to_cpu(READ_ONCE(desc->buf0));
if (len) {
u32 ctl = le32_to_cpu(READ_ONCE(desc->ctrl));
*len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctl);
*more = !(ctl & MT_DMA_CTL_LAST_SEC0);
}
if (info)
*info = le32_to_cpu(desc->info);
dma_unmap_single(dev->dev, buf_addr, buf_len, DMA_FROM_DEVICE);
e->buf = NULL;
return buf;
}
static void *
mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
int *len, u32 *info, bool *more)
{
int idx = q->tail;
*more = false;
if (!q->queued)
return NULL;
if (!flush && !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
return NULL;
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
return mt76_dma_get_buf(dev, q, idx, len, info, more);
}
static void
mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
iowrite32(q->head, &q->regs->cpu_idx);
}
static int
mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi)
{
dma_addr_t addr;
void *buf;
int frames = 0;
int len = SKB_WITH_OVERHEAD(q->buf_size);
int offset = q->buf_offset;
int idx;
void *(*alloc)(unsigned int fragsz);
if (napi)
alloc = napi_alloc_frag;
else
alloc = netdev_alloc_frag;
spin_lock_bh(&q->lock);
while (q->queued < q->ndesc - 1) {
struct mt76_queue_buf qbuf;
buf = alloc(q->buf_size);
if (!buf)
break;
addr = dma_map_single(dev->dev, buf, len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev->dev, addr)) {
skb_free_frag(buf);
break;
}
qbuf.addr = addr + offset;
qbuf.len = len - offset;
idx = mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL);
frames++;
}
if (frames)
mt76_dma_kick_queue(dev, q);
spin_unlock_bh(&q->lock);
return frames;
}
static void
mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
{
void *buf;
bool more;
spin_lock_bh(&q->lock);
do {
buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more);
if (!buf)
break;
skb_free_frag(buf);
} while (1);
spin_unlock_bh(&q->lock);
}
static void
mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
{
struct mt76_queue *q = &dev->q_rx[qid];
int i;
for (i = 0; i < q->ndesc; i++)
q->desc[i].ctrl &= ~cpu_to_le32(MT_DMA_CTL_DMA_DONE);
mt76_dma_rx_cleanup(dev, q);
mt76_dma_sync_idx(dev, q);
mt76_dma_rx_fill(dev, q, false);
}
static void
mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
int len, bool more)
{
struct page *page = virt_to_head_page(data);
int offset = data - page_address(page);
struct sk_buff *skb = q->rx_head;
offset += q->buf_offset;
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, len,
q->buf_size);
if (more)
return;
q->rx_head = NULL;
dev->drv->rx_skb(dev, q - dev->q_rx, skb);
}
static int
mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
{
struct sk_buff *skb;
unsigned char *data;
int len;
int done = 0;
bool more;
while (done < budget) {
u32 info;
data = mt76_dma_dequeue(dev, q, false, &len, &info, &more);
if (!data)
break;
if (q->rx_head) {
mt76_add_fragment(dev, q, data, len, more);
continue;
}
skb = build_skb(data, q->buf_size);
if (!skb) {
skb_free_frag(data);
continue;
}
skb_reserve(skb, q->buf_offset);
if (skb->tail + len > skb->end) {
dev_kfree_skb(skb);
continue;
}
if (q == &dev->q_rx[MT_RXQ_MCU]) {
u32 *rxfce = (u32 *) skb->cb;
*rxfce = info;
}
__skb_put(skb, len);
done++;
if (more) {
q->rx_head = skb;
continue;
}
dev->drv->rx_skb(dev, q - dev->q_rx, skb);
}
mt76_dma_rx_fill(dev, q, true);
return done;
}
static int
mt76_dma_rx_poll(struct napi_struct *napi, int budget)
{
struct mt76_dev *dev;
int qid, done;
dev = container_of(napi->dev, struct mt76_dev, napi_dev);
qid = napi - dev->napi;
done = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget);
if (done < budget) {
napi_complete(napi);
dev->drv->rx_poll_complete(dev, qid);
}
mt76_rx_complete(dev, qid);
return done;
}
static int
mt76_dma_init(struct mt76_dev *dev)
{
int i;
init_dummy_netdev(&dev->napi_dev);
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
netif_napi_add(&dev->napi_dev, &dev->napi[i], mt76_dma_rx_poll,
64);
mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
skb_queue_head_init(&dev->rx_skb[i]);
napi_enable(&dev->napi[i]);
}
return 0;
}
static const struct mt76_queue_ops mt76_dma_ops = {
.init = mt76_dma_init,
.alloc = mt76_dma_alloc_queue,
.add_buf = mt76_dma_add_buf,
.tx_cleanup = mt76_dma_tx_cleanup,
.rx_reset = mt76_dma_rx_reset,
.kick = mt76_dma_kick_queue,
};
int mt76_dma_attach(struct mt76_dev *dev)
{
dev->queue_ops = &mt76_dma_ops;
return 0;
}
EXPORT_SYMBOL_GPL(mt76_dma_attach);
void mt76_dma_cleanup(struct mt76_dev *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++)
mt76_dma_tx_cleanup(dev, i, true);
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
netif_napi_del(&dev->napi[i]);
mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
}
}
EXPORT_SYMBOL_GPL(mt76_dma_cleanup);

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __MT76_DMA_H
#define __MT76_DMA_H
#define MT_RING_SIZE 0x10
#define MT_DMA_CTL_SD_LEN1 GENMASK(13, 0)
#define MT_DMA_CTL_LAST_SEC1 BIT(14)
#define MT_DMA_CTL_BURST BIT(15)
#define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16)
#define MT_DMA_CTL_LAST_SEC0 BIT(30)
#define MT_DMA_CTL_DMA_DONE BIT(31)
struct mt76_desc {
__le32 buf0;
__le32 ctrl;
__le32 buf1;
__le32 info;
} __packed __aligned(4);
int mt76_dma_attach(struct mt76_dev *dev);
void mt76_dma_cleanup(struct mt76_dev *dev);
#endif

View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/etherdevice.h>
#include "mt76.h"
static int
mt76_get_of_eeprom(struct mt76_dev *dev, int len)
{
#if defined(CONFIG_OF) && defined(CONFIG_MTD)
struct device_node *np = dev->dev->of_node;
struct mtd_info *mtd;
const __be32 *list;
const char *part;
phandle phandle;
int offset = 0;
int size;
size_t retlen;
int ret;
if (!np)
return -ENOENT;
list = of_get_property(np, "mediatek,mtd-eeprom", &size);
if (!list)
return -ENOENT;
phandle = be32_to_cpup(list++);
if (!phandle)
return -ENOENT;
np = of_find_node_by_phandle(phandle);
if (!np)
return -EINVAL;
part = of_get_property(np, "label", NULL);
if (!part)
part = np->name;
mtd = get_mtd_device_nm(part);
if (IS_ERR(mtd))
return PTR_ERR(mtd);
if (size <= sizeof(*list))
return -EINVAL;
offset = be32_to_cpup(list);
ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data);
put_mtd_device(mtd);
if (ret)
return ret;
if (retlen < len)
return -EINVAL;
return 0;
#else
return -ENOENT;
#endif
}
void
mt76_eeprom_override(struct mt76_dev *dev)
{
#ifdef CONFIG_OF
struct device_node *np = dev->dev->of_node;
const u8 *mac;
if (!np)
return;
mac = of_get_mac_address(np);
if (mac)
memcpy(dev->macaddr, mac, ETH_ALEN);
#endif
if (!is_valid_ether_addr(dev->macaddr)) {
eth_random_addr(dev->macaddr);
dev_info(dev->dev,
"Invalid MAC address, using random address %pM\n",
dev->macaddr);
}
}
EXPORT_SYMBOL_GPL(mt76_eeprom_override);
int
mt76_eeprom_init(struct mt76_dev *dev, int len)
{
dev->eeprom.size = len;
dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
if (!dev->eeprom.data)
return -ENOMEM;
return !mt76_get_of_eeprom(dev, len);
}
EXPORT_SYMBOL_GPL(mt76_eeprom_init);

View file

@ -0,0 +1,393 @@
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/of.h>
#include "mt76.h"
#define CHAN2G(_idx, _freq) { \
.band = NL80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 30, \
}
#define CHAN5G(_idx, _freq) { \
.band = NL80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 30, \
}
static const struct ieee80211_channel mt76_channels_2ghz[] = {
CHAN2G(1, 2412),
CHAN2G(2, 2417),
CHAN2G(3, 2422),
CHAN2G(4, 2427),
CHAN2G(5, 2432),
CHAN2G(6, 2437),
CHAN2G(7, 2442),
CHAN2G(8, 2447),
CHAN2G(9, 2452),
CHAN2G(10, 2457),
CHAN2G(11, 2462),
CHAN2G(12, 2467),
CHAN2G(13, 2472),
CHAN2G(14, 2484),
};
static const struct ieee80211_channel mt76_channels_5ghz[] = {
CHAN5G(36, 5180),
CHAN5G(40, 5200),
CHAN5G(44, 5220),
CHAN5G(48, 5240),
CHAN5G(52, 5260),
CHAN5G(56, 5280),
CHAN5G(60, 5300),
CHAN5G(64, 5320),
CHAN5G(100, 5500),
CHAN5G(104, 5520),
CHAN5G(108, 5540),
CHAN5G(112, 5560),
CHAN5G(116, 5580),
CHAN5G(120, 5600),
CHAN5G(124, 5620),
CHAN5G(128, 5640),
CHAN5G(132, 5660),
CHAN5G(136, 5680),
CHAN5G(140, 5700),
CHAN5G(149, 5745),
CHAN5G(153, 5765),
CHAN5G(157, 5785),
CHAN5G(161, 5805),
CHAN5G(165, 5825),
};
static const struct ieee80211_tpt_blink mt76_tpt_blink[] = {
{ .throughput = 0 * 1024, .blink_time = 334 },
{ .throughput = 1 * 1024, .blink_time = 260 },
{ .throughput = 5 * 1024, .blink_time = 220 },
{ .throughput = 10 * 1024, .blink_time = 190 },
{ .throughput = 20 * 1024, .blink_time = 170 },
{ .throughput = 50 * 1024, .blink_time = 150 },
{ .throughput = 70 * 1024, .blink_time = 130 },
{ .throughput = 100 * 1024, .blink_time = 110 },
{ .throughput = 200 * 1024, .blink_time = 80 },
{ .throughput = 300 * 1024, .blink_time = 50 },
};
static int mt76_led_init(struct mt76_dev *dev)
{
struct device_node *np = dev->dev->of_node;
struct ieee80211_hw *hw = dev->hw;
int led_pin;
if (!dev->led_cdev.brightness_set && !dev->led_cdev.blink_set)
return 0;
snprintf(dev->led_name, sizeof(dev->led_name),
"mt76-%s", wiphy_name(hw->wiphy));
dev->led_cdev.name = dev->led_name;
dev->led_cdev.default_trigger =
ieee80211_create_tpt_led_trigger(hw,
IEEE80211_TPT_LEDTRIG_FL_RADIO,
mt76_tpt_blink,
ARRAY_SIZE(mt76_tpt_blink));
np = of_get_child_by_name(np, "led");
if (np) {
if (!of_property_read_u32(np, "led-sources", &led_pin))
dev->led_pin = led_pin;
dev->led_al = of_property_read_bool(np, "led-active-low");
}
return devm_led_classdev_register(dev->dev, &dev->led_cdev);
}
static int
mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
const struct ieee80211_channel *chan, int n_chan,
struct ieee80211_rate *rates, int n_rates, bool vht)
{
struct ieee80211_supported_band *sband = &msband->sband;
struct ieee80211_sta_ht_cap *ht_cap;
struct ieee80211_sta_vht_cap *vht_cap;
void *chanlist;
u16 mcs_map;
int size;
size = n_chan * sizeof(*chan);
chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
if (!chanlist)
return -ENOMEM;
msband->chan = devm_kzalloc(dev->dev, n_chan * sizeof(*msband->chan),
GFP_KERNEL);
if (!msband->chan)
return -ENOMEM;
sband->channels = chanlist;
sband->n_channels = n_chan;
sband->bitrates = rates;
sband->n_bitrates = n_rates;
dev->chandef.chan = &sband->channels[0];
ht_cap = &sband->ht_cap;
ht_cap->ht_supported = true;
ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_TX_STBC |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
ht_cap->mcs.rx_mask[0] = 0xff;
ht_cap->mcs.rx_mask[1] = 0xff;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
if (!vht)
return 0;
vht_cap = &sband->vht_cap;
vht_cap->vht_supported = true;
mcs_map = (IEEE80211_VHT_MCS_SUPPORT_0_9 << (0 * 2)) |
(IEEE80211_VHT_MCS_SUPPORT_0_9 << (1 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (2 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (3 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (4 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (5 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (6 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (7 * 2));
vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC |
IEEE80211_VHT_CAP_TXSTBC |
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_SHORT_GI_80;
return 0;
}
static int
mt76_init_sband_2g(struct mt76_dev *dev, struct ieee80211_rate *rates,
int n_rates)
{
dev->hw->wiphy->bands[NL80211_BAND_2GHZ] = &dev->sband_2g.sband;
return mt76_init_sband(dev, &dev->sband_2g,
mt76_channels_2ghz,
ARRAY_SIZE(mt76_channels_2ghz),
rates, n_rates, false);
}
static int
mt76_init_sband_5g(struct mt76_dev *dev, struct ieee80211_rate *rates,
int n_rates, bool vht)
{
dev->hw->wiphy->bands[NL80211_BAND_5GHZ] = &dev->sband_5g.sband;
return mt76_init_sband(dev, &dev->sband_5g,
mt76_channels_5ghz,
ARRAY_SIZE(mt76_channels_5ghz),
rates, n_rates, vht);
}
static void
mt76_check_sband(struct mt76_dev *dev, int band)
{
struct ieee80211_supported_band *sband = dev->hw->wiphy->bands[band];
bool found = false;
int i;
if (!sband)
return;
for (i = 0; i < sband->n_channels; i++) {
if (sband->channels[i].flags & IEEE80211_CHAN_DISABLED)
continue;
found = true;
break;
}
if (found)
return;
sband->n_channels = 0;
dev->hw->wiphy->bands[band] = NULL;
}
int mt76_register_device(struct mt76_dev *dev, bool vht,
struct ieee80211_rate *rates, int n_rates)
{
struct ieee80211_hw *hw = dev->hw;
struct wiphy *wiphy = hw->wiphy;
int ret;
dev_set_drvdata(dev->dev, dev);
spin_lock_init(&dev->lock);
spin_lock_init(&dev->cc_lock);
INIT_LIST_HEAD(&dev->txwi_cache);
SET_IEEE80211_DEV(hw, dev->dev);
SET_IEEE80211_PERM_ADDR(hw, dev->macaddr);
wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
#ifdef CONFIG_MAC80211_MESH
BIT(NL80211_IFTYPE_MESH_POINT) |
#endif
BIT(NL80211_IFTYPE_ADHOC);
wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
hw->txq_data_size = sizeof(struct mt76_txq);
hw->max_tx_fragments = 16;
ieee80211_hw_set(hw, SIGNAL_DBM);
ieee80211_hw_set(hw, PS_NULLFUNC_STACK);
ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, TX_FRAG_LIST);
ieee80211_hw_set(hw, MFP_CAPABLE);
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
if (dev->cap.has_2ghz) {
ret = mt76_init_sband_2g(dev, rates, n_rates);
if (ret)
return ret;
}
if (dev->cap.has_5ghz) {
ret = mt76_init_sband_5g(dev, rates + 4, n_rates - 4, vht);
if (ret)
return ret;
}
wiphy_read_of_freq_limits(dev->hw->wiphy);
mt76_check_sband(dev, NL80211_BAND_2GHZ);
mt76_check_sband(dev, NL80211_BAND_5GHZ);
ret = mt76_led_init(dev);
if (ret)
return ret;
return ieee80211_register_hw(hw);
}
EXPORT_SYMBOL_GPL(mt76_register_device);
void mt76_unregister_device(struct mt76_dev *dev)
{
struct ieee80211_hw *hw = dev->hw;
ieee80211_unregister_hw(hw);
mt76_tx_free(dev);
}
EXPORT_SYMBOL_GPL(mt76_unregister_device);
void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
{
if (!test_bit(MT76_STATE_RUNNING, &dev->state)) {
dev_kfree_skb(skb);
return;
}
__skb_queue_tail(&dev->rx_skb[q], skb);
}
EXPORT_SYMBOL_GPL(mt76_rx);
void mt76_set_channel(struct mt76_dev *dev)
{
struct ieee80211_hw *hw = dev->hw;
struct cfg80211_chan_def *chandef = &hw->conf.chandef;
struct mt76_channel_state *state;
bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (dev->drv->update_survey)
dev->drv->update_survey(dev);
dev->chandef = *chandef;
if (!offchannel)
dev->main_chan = chandef->chan;
if (chandef->chan != dev->main_chan) {
state = mt76_channel_state(dev, chandef->chan);
memset(state, 0, sizeof(*state));
}
}
EXPORT_SYMBOL_GPL(mt76_set_channel);
int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
struct mt76_dev *dev = hw->priv;
struct mt76_sband *sband;
struct ieee80211_channel *chan;
struct mt76_channel_state *state;
int ret = 0;
if (idx == 0 && dev->drv->update_survey)
dev->drv->update_survey(dev);
sband = &dev->sband_2g;
if (idx >= sband->sband.n_channels) {
idx -= sband->sband.n_channels;
sband = &dev->sband_5g;
}
if (idx >= sband->sband.n_channels)
return -ENOENT;
chan = &sband->sband.channels[idx];
state = mt76_channel_state(dev, chan);
memset(survey, 0, sizeof(*survey));
survey->channel = chan;
survey->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY;
if (chan == dev->main_chan)
survey->filled |= SURVEY_INFO_IN_USE;
spin_lock_bh(&dev->cc_lock);
survey->time = div_u64(state->cc_active, 1000);
survey->time_busy = div_u64(state->cc_busy, 1000);
spin_unlock_bh(&dev->cc_lock);
return ret;
}
EXPORT_SYMBOL_GPL(mt76_get_survey);
void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
{
struct sk_buff *skb;
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
ieee80211_rx_napi(dev->hw, NULL, skb, &dev->napi[q]);
}

Some files were not shown because too many files have changed in this diff Show more