diff options
276 files changed, 8620 insertions, 9949 deletions
diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index c9bd0e2b5db7..be90c9e9e5bc 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -655,10 +655,10 @@ static void ath10k_ahb_hif_stop(struct ath10k *ar) ath10k_ahb_irq_disable(ar); synchronize_irq(ar_ahb->irq); - ath10k_pci_flush(ar); - napi_synchronize(&ar->napi); napi_disable(&ar->napi); + + ath10k_pci_flush(ar); } static int ath10k_ahb_hif_power_up(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 18c709c484e7..d0381aaad01c 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -1416,10 +1416,8 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id, nentries = roundup_pow_of_two(nentries); - src_ring = kzalloc(sizeof(*src_ring) + - (nentries * - sizeof(*src_ring->per_transfer_context)), - GFP_KERNEL); + src_ring = kzalloc(struct_size(src_ring, per_transfer_context, + nentries), GFP_KERNEL); if (src_ring == NULL) return ERR_PTR(-ENOMEM); @@ -1476,10 +1474,8 @@ ath10k_ce_alloc_src_ring_64(struct ath10k *ar, unsigned int ce_id, nentries = roundup_pow_of_two(nentries); - src_ring = kzalloc(sizeof(*src_ring) + - (nentries * - sizeof(*src_ring->per_transfer_context)), - GFP_KERNEL); + src_ring = kzalloc(struct_size(src_ring, per_transfer_context, + nentries), GFP_KERNEL); if (!src_ring) return ERR_PTR(-ENOMEM); @@ -1534,10 +1530,8 @@ ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id, nentries = roundup_pow_of_two(attr->dest_nentries); - dest_ring = kzalloc(sizeof(*dest_ring) + - (nentries * - sizeof(*dest_ring->per_transfer_context)), - GFP_KERNEL); + dest_ring = kzalloc(struct_size(dest_ring, per_transfer_context, + nentries), GFP_KERNEL); if (dest_ring == NULL) return ERR_PTR(-ENOMEM); @@ -1580,10 +1574,8 @@ ath10k_ce_alloc_dest_ring_64(struct ath10k *ar, unsigned int ce_id, nentries = roundup_pow_of_two(attr->dest_nentries); - dest_ring = kzalloc(sizeof(*dest_ring) + - (nentries * - sizeof(*dest_ring->per_transfer_context)), - GFP_KERNEL); + dest_ring = kzalloc(struct_size(dest_ring, per_transfer_context, + nentries), GFP_KERNEL); if (!dest_ring) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index c40cd129afe7..ebc213884a9a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -91,6 +91,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA988X_HW_2_0_VERSION, @@ -124,6 +125,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA9887_HW_1_0_VERSION, @@ -157,6 +159,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA6174_HW_2_1_VERSION, @@ -189,6 +192,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA6174_HW_2_1_VERSION, @@ -221,6 +225,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA6174_HW_3_0_VERSION, @@ -253,6 +258,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA6174_HW_3_2_VERSION, @@ -288,6 +294,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA99X0_HW_2_0_DEV_VERSION, @@ -326,6 +333,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA9984_HW_1_0_DEV_VERSION, @@ -369,6 +377,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA9888_HW_2_0_DEV_VERSION, @@ -411,6 +420,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA9377_HW_1_0_DEV_VERSION, @@ -443,6 +453,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA9377_HW_1_1_DEV_VERSION, @@ -477,6 +488,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = QCA4019_HW_1_0_DEV_VERSION, @@ -516,6 +528,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = false, .shadow_reg_support = false, .rri_on_ddr = false, + .hw_filter_reset_required = true, }, { .id = WCN3990_HW_1_0_DEV_VERSION, @@ -539,6 +552,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .per_ce_irq = true, .shadow_reg_support = true, .rri_on_ddr = true, + .hw_filter_reset_required = false, }, }; @@ -2405,7 +2419,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, * possible to implicitly make it correct by creating a dummy vdev and * then deleting it. */ - if (mode == ATH10K_FIRMWARE_MODE_NORMAL) { + if (ar->hw_params.hw_filter_reset_required && + mode == ATH10K_FIRMWARE_MODE_NORMAL) { status = ath10k_core_reset_rx_filter(ar); if (status) { ath10k_err(ar, diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 4d1cd90d6d27..1455007f3eb8 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1176,11 +1176,11 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, */ /* This probably shouldn't happen but warn just in case */ - if (unlikely(WARN_ON_ONCE(!is_first))) + if (WARN_ON_ONCE(!is_first)) return; /* This probably shouldn't happen but warn just in case */ - if (unlikely(WARN_ON_ONCE(!(is_first && is_last)))) + if (WARN_ON_ONCE(!(is_first && is_last))) return; skb_trim(msdu, msdu->len - FCS_LEN); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 977f79ebb4fd..fac58c3c576a 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -589,6 +589,11 @@ struct ath10k_hw_params { /* Number of bytes to be the offset for each FFT sample */ int spectral_bin_offset; + + /* targets which require hw filter reset during boot up, + * to avoid it sending spurious acks. + */ + bool hw_filter_reset_required; }; struct htt_rx_desc; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 90f9372dec25..496772d95d11 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -18,6 +18,7 @@ #include "mac.h" +#include <net/cfg80211.h> #include <net/mac80211.h> #include <linux/etherdevice.h> #include <linux/acpi.h> @@ -8359,6 +8360,7 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band; } + wiphy_read_of_freq_limits(ar->hw->wiphy); ath10k_mac_setup_ht_vht_cap(ar); ar->hw->wiphy->interface_modes = diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index af2cf55c4c1e..97fa5c74f2fe 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2068,9 +2068,9 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_irq_disable(ar); ath10k_pci_irq_sync(ar); - ath10k_pci_flush(ar); napi_synchronize(&ar->napi); napi_disable(&ar->napi); + ath10k_pci_flush(ar); spin_lock_irqsave(&ar_pci->ps_lock, flags); WARN_ON(ar_pci->ps_wake_refcount > 0); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index fd612d2905b0..40ce0e4006bc 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1869,6 +1869,12 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) if (ret) dev_kfree_skb_any(skb); + if (ret == -EAGAIN) { + ath10k_warn(ar, "wmi command %d timeout, restarting hardware\n", + cmd_id); + queue_work(ar->workqueue, &ar->restart_work); + } + return ret; } diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index e01faf641288..94f70047d3fc 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -1028,8 +1028,6 @@ ath5k_debug_dump_bands(struct ath5k_hw *ah) if (likely(!(ah->debug.level & ATH5K_DEBUG_DUMPBANDS))) return; - BUG_ON(!ah->sbands); - for (b = 0; b < NUM_NL80211_BANDS; b++) { struct ieee80211_supported_band *band = &ah->sbands[b]; char bname[6]; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 0c61dbaa62a4..cb59016c723b 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -638,7 +638,7 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, memcpy(vif->bssid, bssid, sizeof(vif->bssid)); vif->bss_ch = channel; - if ((vif->nw_type == INFRA_NETWORK)) { + if (vif->nw_type == INFRA_NETWORK) { ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); ath6kl_check_ch_switch(ar, channel); diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index ef2dd68d3f77..11d6f975c87d 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -37,10 +37,6 @@ #define AR5008_11NG_HT_SS_SHIFT 12 #define AR5008_11NG_HT_DS_SHIFT 20 -static const int firstep_table[] = -/* level: 0 1 2 3 4 5 6 7 8 */ - { -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */ - /* * register values to turn OFDM weak signal detection OFF */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 21ba20981a80..003e9fb456ac 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -1074,7 +1074,6 @@ struct ath_softc { struct ath_spec_scan_priv spec_priv; - struct ieee80211_vif *tx99_vif; struct sk_buff *tx99_skb; bool tx99_state; s16 tx99_power; diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index a6f45f1bb5bb..ed8b77a74630 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -116,7 +116,7 @@ void ath_debug_rate_stats(struct ath_softc *sc, if (rxs->rate_idx >= ARRAY_SIZE(rstats->ht_stats)) goto exit; - if ((rxs->bw == RATE_INFO_BW_40)) + if (rxs->bw == RATE_INFO_BW_40) rstats->ht_stats[rxs->rate_idx].ht40_cnt++; else rstats->ht_stats[rxs->rate_idx].ht20_cnt++; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1049773378f2..6ce4b9f1dcb4 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1251,15 +1251,10 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, struct ath_vif *avp = (void *)vif->drv_priv; struct ath_node *an = &avp->mcast_node; - mutex_lock(&sc->mutex); + if (IS_ENABLED(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; - if (IS_ENABLED(CONFIG_ATH9K_TX99)) { - if (sc->cur_chan->nvifs >= 1) { - mutex_unlock(&sc->mutex); - return -EOPNOTSUPP; - } - sc->tx99_vif = vif; - } + mutex_lock(&sc->mutex); ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->cur_chan->nvifs++; @@ -1342,7 +1337,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ath9k_p2p_remove_vif(sc, vif); sc->cur_chan->nvifs--; - sc->tx99_vif = NULL; if (!ath9k_is_chanctx_enabled()) list_del(&avp->list); diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index ce50d8f5835e..9b05ffb68c34 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -54,12 +54,6 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info; struct sk_buff *skb; - struct ath_vif *avp; - - if (!sc->tx99_vif) - return NULL; - - avp = (struct ath_vif *)sc->tx99_vif->drv_priv; skb = alloc_skb(len, GFP_KERNEL); if (!skb) @@ -77,14 +71,11 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); - hdr->seq_ctrl |= cpu_to_le16(avp->seq_no); - tx_info = IEEE80211_SKB_CB(skb); memset(tx_info, 0, sizeof(*tx_info)); rate = &tx_info->control.rates[0]; tx_info->band = sc->cur_chan->chandef.chan->band; tx_info->flags = IEEE80211_TX_CTL_NO_ACK; - tx_info->control.vif = sc->tx99_vif; rate->count = 1; if (ah->curchan && IS_CHAN_HT(ah->curchan)) { rate->flags |= IEEE80211_TX_RC_MCS; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 43b6c8508e49..66b6a8872c06 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2973,7 +2973,7 @@ int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, return -EINVAL; } - ath_set_rates(sc->tx99_vif, NULL, bf); + ath_set_rates(NULL, NULL, bf); ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum); diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 0cb5b58925dc..8c75651ede6c 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -246,8 +246,8 @@ static void carl9170_release_dev_space(struct ar9170 *ar, struct sk_buff *skb) * of available memory blocks, so the number can * never execeed the mem_blocks count. */ - if (unlikely(WARN_ON_ONCE(cookie == 0) || - WARN_ON_ONCE(cookie > ar->fw.mem_blocks))) + if (WARN_ON_ONCE(cookie == 0) || + WARN_ON_ONCE(cookie > ar->fw.mem_blocks)) return; atomic_add(DIV_ROUND_UP(skb->len, ar->fw.mem_block_size), diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 06cfe8d311f3..5ab3e31c9ffa 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -174,13 +174,12 @@ static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn int i; size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc); - wcn_ch->cpu_addr = dma_alloc_coherent(dev, size, &wcn_ch->dma_addr, - GFP_KERNEL); + wcn_ch->cpu_addr = dma_zalloc_coherent(dev, size, + &wcn_ch->dma_addr, + GFP_KERNEL); if (!wcn_ch->cpu_addr) return -ENOMEM; - memset(wcn_ch->cpu_addr, 0, size); - cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr; cur_ctl = wcn_ch->head_blk_ctl; @@ -628,13 +627,13 @@ int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) 16 - (WCN36XX_BD_CHUNK_SIZE % 8); s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H; - cpu_addr = dma_alloc_coherent(wcn->dev, s, &wcn->mgmt_mem_pool.phy_addr, - GFP_KERNEL); + cpu_addr = dma_zalloc_coherent(wcn->dev, s, + &wcn->mgmt_mem_pool.phy_addr, + GFP_KERNEL); if (!cpu_addr) goto out_err; wcn->mgmt_mem_pool.virt_addr = cpu_addr; - memset(cpu_addr, 0, s); /* Allocate BD headers for DATA frames */ @@ -643,13 +642,13 @@ int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) 16 - (WCN36XX_BD_CHUNK_SIZE % 8); s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L; - cpu_addr = dma_alloc_coherent(wcn->dev, s, &wcn->data_mem_pool.phy_addr, - GFP_KERNEL); + cpu_addr = dma_zalloc_coherent(wcn->dev, s, + &wcn->data_mem_pool.phy_addr, + GFP_KERNEL); if (!cpu_addr) goto out_err; wcn->data_mem_pool.virt_addr = cpu_addr; - memset(cpu_addr, 0, s); return 0; diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 00098f24116d..1d2d698fb779 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -792,10 +792,10 @@ static int wcn36xx_smd_process_ptt_msg_rsp(void *buf, size_t len, rsp->header.len - sizeof(rsp->ptt_msg_resp_status)); if (rsp->header.len > 0) { - *p_ptt_rsp_msg = kmalloc(rsp->header.len, GFP_ATOMIC); + *p_ptt_rsp_msg = kmemdup(rsp->ptt_msg, rsp->header.len, + GFP_ATOMIC); if (!*p_ptt_rsp_msg) return -ENOMEM; - memcpy(*p_ptt_rsp_msg, rsp->ptt_msg, rsp->header.len); } return ret; } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index f79c337105cb..d18e81fae5f1 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -48,9 +48,29 @@ static struct ieee80211_channel wil_60ghz_channels[] = { CHAN60G(1, 0), CHAN60G(2, 0), CHAN60G(3, 0), -/* channel 4 not supported yet */ + CHAN60G(4, 0), }; +static int wil_num_supported_channels(struct wil6210_priv *wil) +{ + int num_channels = ARRAY_SIZE(wil_60ghz_channels); + + if (!test_bit(WMI_FW_CAPABILITY_CHANNEL_4, wil->fw_capabilities)) + num_channels--; + + return num_channels; +} + +void update_supported_bands(struct wil6210_priv *wil) +{ + struct wiphy *wiphy = wil_to_wiphy(wil); + + wil_dbg_misc(wil, "update supported bands"); + + wiphy->bands[NL80211_BAND_60GHZ]->n_channels = + wil_num_supported_channels(wil); +} + /* Vendor id to be used in vendor specific command and events * to user space. * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID, @@ -199,7 +219,9 @@ wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_RESP >> 4) | BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) | - BIT(IEEE80211_STYPE_DISASSOC >> 4), + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_REASSOC_RESP >> 4), .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | @@ -871,6 +893,26 @@ static void wil_print_crypto(struct wil6210_priv *wil, c->control_port_no_encrypt); } +static const char * +wil_get_auth_type_name(enum nl80211_auth_type auth_type) +{ + switch (auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + return "OPEN_SYSTEM"; + case NL80211_AUTHTYPE_SHARED_KEY: + return "SHARED_KEY"; + case NL80211_AUTHTYPE_FT: + return "FT"; + case NL80211_AUTHTYPE_NETWORK_EAP: + return "NETWORK_EAP"; + case NL80211_AUTHTYPE_SAE: + return "SAE"; + case NL80211_AUTHTYPE_AUTOMATIC: + return "AUTOMATIC"; + default: + return "unknown"; + } +} static void wil_print_connect_params(struct wil6210_priv *wil, struct cfg80211_connect_params *sme) { @@ -884,11 +926,73 @@ static void wil_print_connect_params(struct wil6210_priv *wil, if (sme->ssid) print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET, 16, 1, sme->ssid, sme->ssid_len, true); + if (sme->prev_bssid) + wil_info(wil, " Previous BSSID=%pM\n", sme->prev_bssid); + wil_info(wil, " Auth Type: %s\n", + wil_get_auth_type_name(sme->auth_type)); wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open"); wil_info(wil, " PBSS: %d\n", sme->pbss); wil_print_crypto(wil, &sme->crypto); } +static int wil_ft_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(ndev); + struct wmi_ft_auth_cmd auth_cmd; + int rc; + + if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) { + wil_err(wil, "FT: FW does not support FT roaming\n"); + return -EOPNOTSUPP; + } + + if (!sme->prev_bssid) { + wil_err(wil, "FT: prev_bssid was not set\n"); + return -EINVAL; + } + + if (ether_addr_equal(sme->prev_bssid, sme->bssid)) { + wil_err(wil, "FT: can not roam to same AP\n"); + return -EINVAL; + } + + if (!test_bit(wil_vif_fwconnected, vif->status)) { + wil_err(wil, "FT: roam while not connected\n"); + return -EINVAL; + } + + if (vif->privacy != sme->privacy) { + wil_err(wil, "FT: privacy mismatch, current (%d) roam (%d)\n", + vif->privacy, sme->privacy); + return -EINVAL; + } + + if (sme->pbss) { + wil_err(wil, "FT: roam is not valid for PBSS\n"); + return -EINVAL; + } + + memset(&auth_cmd, 0, sizeof(auth_cmd)); + auth_cmd.channel = sme->channel->hw_value - 1; + ether_addr_copy(auth_cmd.bssid, sme->bssid); + + wil_info(wil, "FT: roaming\n"); + + set_bit(wil_vif_ft_roam, vif->status); + rc = wmi_send(wil, WMI_FT_AUTH_CMDID, vif->mid, + &auth_cmd, sizeof(auth_cmd)); + if (rc == 0) + mod_timer(&vif->connect_timer, + jiffies + msecs_to_jiffies(5000)); + else + clear_bit(wil_vif_ft_roam, vif->status); + + return rc; +} + static int wil_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_connect_params *sme) @@ -901,14 +1005,23 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, const u8 *rsn_eid; int ch; int rc = 0; + bool is_ft_roam = false; + u8 network_type; enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS; wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid); wil_print_connect_params(wil, sme); - if (test_bit(wil_vif_fwconnecting, vif->status) || + if (sme->auth_type == NL80211_AUTHTYPE_FT) + is_ft_roam = true; + if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC && test_bit(wil_vif_fwconnected, vif->status)) - return -EALREADY; + is_ft_roam = true; + + if (!is_ft_roam) + if (test_bit(wil_vif_fwconnecting, vif->status) || + test_bit(wil_vif_fwconnected, vif->status)) + return -EALREADY; if (sme->ie_len > WMI_MAX_IE_LEN) { wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len); @@ -918,8 +1031,13 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, rsn_eid = sme->ie ? cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : NULL; - if (sme->privacy && !rsn_eid) + if (sme->privacy && !rsn_eid) { wil_info(wil, "WSC connection\n"); + if (is_ft_roam) { + wil_err(wil, "No WSC with FT roam\n"); + return -EINVAL; + } + } if (sme->pbss) bss_type = IEEE80211_BSS_TYPE_PBSS; @@ -941,6 +1059,45 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, vif->privacy = sme->privacy; vif->pbss = sme->pbss; + rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); + if (rc) + goto out; + + switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) { + case WLAN_CAPABILITY_DMG_TYPE_AP: + network_type = WMI_NETTYPE_INFRA; + break; + case WLAN_CAPABILITY_DMG_TYPE_PBSS: + network_type = WMI_NETTYPE_P2P; + break; + default: + wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", + bss->capability); + rc = -EINVAL; + goto out; + } + + ch = bss->channel->hw_value; + if (ch == 0) { + wil_err(wil, "BSS at unknown frequency %dMhz\n", + bss->channel->center_freq); + rc = -EOPNOTSUPP; + goto out; + } + + if (is_ft_roam) { + if (network_type != WMI_NETTYPE_INFRA) { + wil_err(wil, "FT: Unsupported BSS type, capability= 0x%04x\n", + bss->capability); + rc = -EINVAL; + goto out; + } + rc = wil_ft_connect(wiphy, ndev, sme); + if (rc == 0) + vif->bss = bss; + goto out; + } + if (vif->privacy) { /* For secure assoc, remove old keys */ rc = wmi_del_cipher_key(vif, 0, bss->bssid, @@ -957,28 +1114,9 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, } } - /* WMI_SET_APPIE_CMD. ie may contain rsn info as well as other info - * elements. Send it also in case it's empty, to erase previously set - * ies in FW. - */ - rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); - if (rc) - goto out; - /* WMI_CONNECT_CMD */ memset(&conn, 0, sizeof(conn)); - switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) { - case WLAN_CAPABILITY_DMG_TYPE_AP: - conn.network_type = WMI_NETTYPE_INFRA; - break; - case WLAN_CAPABILITY_DMG_TYPE_PBSS: - conn.network_type = WMI_NETTYPE_P2P; - break; - default: - wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", - bss->capability); - goto out; - } + conn.network_type = network_type; if (vif->privacy) { if (rsn_eid) { /* regular secure connection */ conn.dot11_auth_mode = WMI_AUTH11_SHARED; @@ -998,14 +1136,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, conn.ssid_len = min_t(u8, ssid_eid[1], 32); memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); - - ch = bss->channel->hw_value; - if (ch == 0) { - wil_err(wil, "BSS at unknown frequency %dMhz\n", - bss->channel->center_freq); - rc = -EOPNOTSUPP; - goto out; - } conn.channel = ch - 1; ether_addr_copy(conn.bssid, bss->bssid); @@ -1201,9 +1331,9 @@ wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid, return &wil->sta[cid]; } -static void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage, - struct wil_sta_info *cs, - struct key_params *params) +void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage, + struct wil_sta_info *cs, + struct key_params *params) { struct wil_tid_crypto_rx_single *cc; int tid; @@ -1286,13 +1416,19 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy, params->seq_len, params->seq); if (IS_ERR(cs)) { - wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n", - mac_addr, key_usage_str[key_usage], key_index, - params->seq_len, params->seq); - return -EINVAL; + /* in FT, sta info may not be available as add_key may be + * sent by host before FW sends WMI_CONNECT_EVENT + */ + if (!test_bit(wil_vif_ft_roam, vif->status)) { + wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n", + mac_addr, key_usage_str[key_usage], key_index, + params->seq_len, params->seq); + return -EINVAL; + } } - wil_del_rx_key(key_index, key_usage, cs); + if (!IS_ERR(cs)) + wil_del_rx_key(key_index, key_usage, cs); if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) { wil_err(wil, @@ -1305,7 +1441,10 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy, rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len, params->key, key_usage); - if (!rc) + if (!rc && !IS_ERR(cs)) + /* in FT set crypto will take place upon receiving + * WMI_RING_EN_EVENTID event + */ wil_set_crypto_rx(key_index, key_usage, cs, params); return rc; @@ -1468,21 +1607,36 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b) } /* internal functions for device reset and starting AP */ -static int _wil_cfg80211_set_ies(struct wil6210_vif *vif, - struct cfg80211_beacon_data *bcon) +static u8 * +_wil_cfg80211_get_proberesp_ies(const u8 *proberesp, u16 proberesp_len, + u16 *ies_len) { - int rc; - u16 len = 0, proberesp_len = 0; - u8 *ies = NULL, *proberesp = NULL; + u8 *ies = NULL; - if (bcon->probe_resp) { + if (proberesp) { struct ieee80211_mgmt *f = - (struct ieee80211_mgmt *)bcon->probe_resp; + (struct ieee80211_mgmt *)proberesp; size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - proberesp = f->u.probe_resp.variable; - proberesp_len = bcon->probe_resp_len - hlen; + + ies = f->u.probe_resp.variable; + if (ies_len) + *ies_len = proberesp_len - hlen; } + + return ies; +} + +static int _wil_cfg80211_set_ies(struct wil6210_vif *vif, + struct cfg80211_beacon_data *bcon) +{ + int rc; + u16 len = 0, proberesp_len = 0; + u8 *ies = NULL, *proberesp; + + proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp, + bcon->probe_resp_len, + &proberesp_len); rc = _wil_cfg80211_merge_extra_ies(proberesp, proberesp_len, bcon->proberesp_ies, @@ -1526,6 +1680,9 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, struct wireless_dev *wdev = ndev->ieee80211_ptr; u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO); + u16 proberesp_len = 0; + u8 *proberesp; + bool ft = false; if (pbss) wmi_nettype = WMI_NETTYPE_P2P; @@ -1538,6 +1695,25 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, wil_set_recovery_state(wil, fw_recovery_idle); + proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp, + bcon->probe_resp_len, + &proberesp_len); + /* check that the probe response IEs has a MDE */ + if ((proberesp && proberesp_len > 0 && + cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN, + proberesp, + proberesp_len))) + ft = true; + + if (ft) { + if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, + wil->fw_capabilities)) { + wil_err(wil, "FW does not support FT roaming\n"); + return -ENOTSUPP; + } + set_bit(wil_vif_ft_roam, vif->status); + } + mutex_lock(&wil->mutex); if (!wil_has_other_active_ifaces(wil, ndev, true, false)) { @@ -1699,6 +1875,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, mutex_lock(&wil->mutex); wmi_pcp_stop(vif); + clear_bit(wil_vif_ft_roam, vif->status); if (last) __wil_down(wil); @@ -1718,8 +1895,9 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy, struct wil6210_vif *vif = ndev_to_vif(dev); struct wil6210_priv *wil = wiphy_to_wil(wiphy); - wil_dbg_misc(wil, "add station %pM aid %d mid %d\n", - mac, params->aid, vif->mid); + wil_dbg_misc(wil, "add station %pM aid %d mid %d mask 0x%x set 0x%x\n", + mac, params->aid, vif->mid, + params->sta_flags_mask, params->sta_flags_set); if (!disable_ap_sme) { wil_err(wil, "not supported with AP SME enabled\n"); @@ -2040,6 +2218,54 @@ wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int +wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil6210_vif *vif = ndev_to_vif(dev); + struct cfg80211_bss *bss; + struct wmi_ft_reassoc_cmd reassoc; + int rc = 0; + + wil_dbg_misc(wil, "update ft ies, mid=%d\n", vif->mid); + wil_hex_dump_misc("FT IE ", DUMP_PREFIX_OFFSET, 16, 1, + ftie->ie, ftie->ie_len, true); + + if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) { + wil_err(wil, "FW does not support FT roaming\n"); + return -EOPNOTSUPP; + } + + rc = wmi_update_ft_ies(vif, ftie->ie_len, ftie->ie); + if (rc) + return rc; + + if (!test_bit(wil_vif_ft_roam, vif->status)) + /* vif is not roaming */ + return 0; + + /* wil_vif_ft_roam is set. wil_cfg80211_update_ft_ies is used as + * a trigger for reassoc + */ + + bss = vif->bss; + if (!bss) { + wil_err(wil, "FT: bss is NULL\n"); + return -EINVAL; + } + + memset(&reassoc, 0, sizeof(reassoc)); + ether_addr_copy(reassoc.bssid, bss->bssid); + + rc = wmi_send(wil, WMI_FT_REASSOC_CMDID, vif->mid, + &reassoc, sizeof(reassoc)); + if (rc) + wil_err(wil, "FT: reassoc failed (%d)\n", rc); + + return rc; +} + static const struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, @@ -2075,6 +2301,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .resume = wil_cfg80211_resume, .sched_scan_start = wil_cfg80211_sched_scan_start, .sched_scan_stop = wil_cfg80211_sched_scan_stop, + .update_ft_ies = wil_cfg80211_update_ft_ies, }; static void wil_wiphy_init(struct wiphy *wiphy) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 49533f884993..66ffae2de86e 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -725,32 +725,6 @@ struct dentry *wil_debugfs_create_ioblob(const char *name, return debugfs_create_file(name, mode, parent, wil_blob, &fops_ioblob); } -/*---reset---*/ -static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - struct wil6210_priv *wil = file->private_data; - struct net_device *ndev = wil->main_ndev; - - /** - * BUG: - * this code does NOT sync device state with the rest of system - * use with care, debug only!!! - */ - rtnl_lock(); - dev_close(ndev); - ndev->flags &= ~IFF_UP; - rtnl_unlock(); - wil_reset(wil, true); - - return len; -} - -static const struct file_operations fops_reset = { - .write = wil_write_file_reset, - .open = simple_open, -}; - /*---write channel 1..4 to rxon for it, 0 to rxoff---*/ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, size_t len, loff_t *ppos) @@ -1263,6 +1237,9 @@ static int wil_rx_buff_mgmt_debugfs_show(struct seq_file *s, void *data) int num_active; int num_free; + if (!rbm->buff_arr) + return -EINVAL; + seq_printf(s, " size = %zu\n", rbm->size); seq_printf(s, " free_list_empty_cnt = %lu\n", rbm->free_list_empty_cnt); @@ -1695,6 +1672,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) char *status = "unknown"; u8 aid = 0; u8 mid; + bool sta_connected = false; switch (p->status) { case wil_sta_unused: @@ -1709,8 +1687,20 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) break; } mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; - seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status, - mid, aid); + if (mid < wil->max_vifs) { + struct wil6210_vif *vif = wil->vifs[mid]; + + if (vif->wdev.iftype == NL80211_IFTYPE_STATION && + p->status == wil_sta_connected) + sta_connected = true; + } + /* print roam counter only for connected stations */ + if (sta_connected) + seq_printf(s, "[%d] %pM connected (roam counter %d) MID %d AID %d\n", + i, p->addr, p->stats.ft_roams, mid, aid); + else + seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, + p->addr, status, mid, aid); if (p->status == wil_sta_connected) { spin_lock_bh(&p->tid_rx_lock); @@ -2451,7 +2441,6 @@ static const struct { {"desc", 0444, &fops_txdesc}, {"bf", 0444, &fops_bf}, {"mem_val", 0644, &fops_memread}, - {"reset", 0244, &fops_reset}, {"rxon", 0244, &fops_rxon}, {"tx_mgmt", 0244, &fops_txmgmt}, {"wmi_send", 0244, &fops_wmi}, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 7debed6bec06..398900a1c29e 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -223,6 +223,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) struct net_device *ndev = vif_to_ndev(vif); struct wireless_dev *wdev = vif_to_wdev(vif); struct wil_sta_info *sta = &wil->sta[cid]; + int min_ring_id = wil_get_min_tx_ring_id(wil); might_sleep(); wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n", @@ -273,7 +274,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) memset(sta->tid_crypto_rx, 0, sizeof(sta->tid_crypto_rx)); memset(&sta->group_crypto_rx, 0, sizeof(sta->group_crypto_rx)); /* release vrings */ - for (i = 0; i < ARRAY_SIZE(wil->ring_tx); i++) { + for (i = min_ring_id; i < ARRAY_SIZE(wil->ring_tx); i++) { if (wil->ring2cid_tid[i][0] == cid) wil_ring_fini_tx(wil, i); } @@ -360,6 +361,8 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, vif->bss = NULL; } clear_bit(wil_vif_fwconnecting, vif->status); + clear_bit(wil_vif_ft_roam, vif->status); + break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: @@ -604,8 +607,10 @@ int wil_priv_init(struct wil6210_priv *wil) wil->sta[i].mid = U8_MAX; } - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { spin_lock_init(&wil->ring_tx_data[i].lock); + wil->ring2cid_tid[i][0] = WIL6210_MAX_CID; + } mutex_init(&wil->mutex); mutex_init(&wil->vif_mutex); @@ -653,8 +658,6 @@ int wil_priv_init(struct wil6210_priv *wil) /* edma configuration can be updated via debugfs before allocation */ wil->num_rx_status_rings = WIL_DEFAULT_NUM_RX_STATUS_RINGS; - wil->use_compressed_rx_status = true; - wil->use_rx_hw_reordering = true; wil->tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT; /* Rx status ring size should be bigger than the number of RX buffers @@ -1154,6 +1157,8 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil) wil->max_agg_wsize = WIL_MAX_AGG_WSIZE; wil->max_ampdu_size = WIL_MAX_AMPDU_SIZE; } + + update_supported_bands(wil); } void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 89119e7facd0..c8c6613371d1 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -108,6 +108,7 @@ int wil_set_capabilities(struct wil6210_priv *wil) set_bit(hw_capa_no_flash, wil->hw_capa); wil->use_enhanced_dma_hw = true; wil->use_rx_hw_reordering = true; + wil->use_compressed_rx_status = true; wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN : WIL_FW_NAME_TALYN; if (wil_fw_verify_file_exists(wil, wil_fw_name)) diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index b608aa16b4f1..983bd001b53b 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -382,11 +382,13 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) } /* apply */ - r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn); - spin_lock_bh(&sta->tid_rx_lock); - wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]); - sta->tid_rx[tid] = r; - spin_unlock_bh(&sta->tid_rx_lock); + if (!wil->use_rx_hw_reordering) { + r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn); + spin_lock_bh(&sta->tid_rx_lock); + wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]); + sta->tid_rx[tid] = r; + spin_unlock_bh(&sta->tid_rx_lock); + } out: return rc; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 6a7943e487fb..cc5f263cc965 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -77,8 +77,9 @@ bool wil_is_tx_idle(struct wil6210_priv *wil) { int i; unsigned long data_comp_to; + int min_ring_id = wil_get_min_tx_ring_id(wil); - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { struct wil_ring *vring = &wil->ring_tx[i]; int vring_index = vring - wil->ring_tx; struct wil_ring_tx_data *txdata = @@ -765,7 +766,14 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) return; } - if (wdev->iftype == NL80211_IFTYPE_AP && !vif->ap_isolate) { + if (wdev->iftype == NL80211_IFTYPE_STATION) { + if (mcast && ether_addr_equal(eth->h_source, ndev->dev_addr)) { + /* mcast packet looped back to us */ + rc = GRO_DROP; + dev_kfree_skb(skb); + goto stats; + } + } else if (wdev->iftype == NL80211_IFTYPE_AP && !vif->ap_isolate) { if (mcast) { /* send multicast frames both to higher layers in * local net stack and back to the wireless medium @@ -1051,6 +1059,88 @@ static int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, return rc; } +static int wil_tx_vring_modify(struct wil6210_vif *vif, int ring_id, int cid, + int tid) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + int rc; + struct wmi_vring_cfg_cmd cmd = { + .action = cpu_to_le32(WMI_VRING_CMD_MODIFY), + .vring_cfg = { + .tx_sw_ring = { + .max_mpdu_size = + cpu_to_le16(wil_mtu2macbuf(mtu_max)), + .ring_size = 0, + }, + .ringid = ring_id, + .cidxtid = mk_cidxtid(cid, tid), + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + .mac_ctrl = 0, + .to_resolution = 0, + .agg_max_wsize = 0, + .schd_params = { + .priority = cpu_to_le16(0), + .timeslot_us = cpu_to_le16(0xfff), + }, + }, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_vring_cfg_done_event cmd; + } __packed reply = { + .cmd = {.status = WMI_FW_STATUS_FAILURE}, + }; + struct wil_ring *vring = &wil->ring_tx[ring_id]; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; + + wil_dbg_misc(wil, "vring_modify: ring %d cid %d tid %d\n", ring_id, + cid, tid); + lockdep_assert_held(&wil->mutex); + + if (!vring->va) { + wil_err(wil, "Tx ring [%d] not allocated\n", ring_id); + return -EINVAL; + } + + if (wil->ring2cid_tid[ring_id][0] != cid || + wil->ring2cid_tid[ring_id][1] != tid) { + wil_err(wil, "ring info does not match cid=%u tid=%u\n", + wil->ring2cid_tid[ring_id][0], + wil->ring2cid_tid[ring_id][1]); + } + + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + + rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + if (rc) + goto fail; + + if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "Tx modify failed, status 0x%02x\n", + reply.cmd.status); + rc = -EINVAL; + goto fail; + } + + /* set BA aggregation window size to 0 to force a new BA with the + * new AP + */ + txdata->agg_wsize = 0; + if (txdata->dot1x_open && agg_wsize >= 0) + wil_addba_tx_request(wil, ring_id, agg_wsize); + + return 0; +fail: + spin_lock_bh(&txdata->lock); + txdata->dot1x_open = false; + txdata->enabled = 0; + spin_unlock_bh(&txdata->lock); + wil->ring2cid_tid[ring_id][0] = WIL6210_MAX_CID; + wil->ring2cid_tid[ring_id][1] = 0; + return rc; +} + int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) { struct wil6210_priv *wil = vif_to_wil(vif); @@ -1935,6 +2025,7 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil, bool check_stop) { int i; + int min_ring_id = wil_get_min_tx_ring_id(wil); if (unlikely(!vif)) return; @@ -1967,7 +2058,7 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil, return; /* check wake */ - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { struct wil_ring *cur_ring = &wil->ring_tx[i]; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i]; @@ -2272,6 +2363,7 @@ void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil) wil->txrx_ops.ring_init_bcast = wil_vring_init_bcast; wil->txrx_ops.tx_init = wil_tx_init; wil->txrx_ops.tx_fini = wil_tx_fini; + wil->txrx_ops.tx_ring_modify = wil_tx_vring_modify; /* RX ops */ wil->txrx_ops.rx_init = wil_rx_init; wil->txrx_ops.wmi_addba_rx_resp = wmi_addba_rx_resp; diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index bca61cb44c37..2bbae75b9a84 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -279,9 +279,6 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, u16 buff_id; *d = *_d; - pa = wil_rx_desc_get_addr_edma(&d->dma); - dmalen = le16_to_cpu(d->dma.length); - dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); /* Extract the SKB from the rx_buff management array */ buff_id = __le16_to_cpu(d->mac.buff_id); @@ -291,10 +288,15 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, } skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; - if (unlikely(!skb)) + if (unlikely(!skb)) { wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); - else + } else { + pa = wil_rx_desc_get_addr_edma(&d->dma); + dmalen = le16_to_cpu(d->dma.length); + dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); + kfree_skb(skb); + } /* Move the buffer from the active to the free list */ list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, @@ -745,6 +747,16 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, return rc; } +static int wil_tx_ring_modify_edma(struct wil6210_vif *vif, int ring_id, + int cid, int tid) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + + wil_err(wil, "ring modify is not supported for EDMA\n"); + + return -EOPNOTSUPP; +} + /* This function is used only for RX SW reorder */ static int wil_check_bar(struct wil6210_priv *wil, void *msg, int cid, struct sk_buff *skb, struct wil_net_stats *stats) @@ -906,6 +918,9 @@ again: wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; if (!skb) { wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); + /* Move the buffer from the active list to the free list */ + list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, + &wil->rx_buff_mgmt.free); goto again; } @@ -1595,6 +1610,7 @@ void wil_init_txrx_ops_edma(struct wil6210_priv *wil) wil->txrx_ops.tx_desc_map = wil_tx_desc_map_edma; wil->txrx_ops.tx_desc_unmap = wil_tx_desc_unmap_edma; wil->txrx_ops.tx_ring_tso = __wil_tx_ring_tso_edma; + wil->txrx_ops.tx_ring_modify = wil_tx_ring_modify_edma; /* RX ops */ wil->txrx_ops.rx_init = wil_rx_init_edma; wil->txrx_ops.wmi_addba_rx_resp = wmi_addba_rx_resp_edma; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 17c294b1ead1..cf6a69198b5d 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -449,6 +449,15 @@ static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid) *tid = (cidxtid >> 4) & 0xf; } +/** + * wil_cid_valid - check cid is valid + * @cid: CID value + */ +static inline bool wil_cid_valid(u8 cid) +{ + return (cid >= 0 && cid < WIL6210_MAX_CID); +} + struct wil6210_mbox_ring { u32 base; u16 entry_size; /* max. size of mbox entry, incl. all headers */ @@ -577,6 +586,7 @@ struct wil_net_stats { unsigned long rx_csum_err; u16 last_mcs_rx; u64 rx_per_mcs[WIL_MCS_MAX + 1]; + u32 ft_roams; /* relevant in STA mode */ }; /** @@ -599,6 +609,8 @@ struct wil_txrx_ops { struct wil_ctx *ctx); int (*tx_ring_tso)(struct wil6210_priv *wil, struct wil6210_vif *vif, struct wil_ring *ring, struct sk_buff *skb); + int (*tx_ring_modify)(struct wil6210_vif *vif, int ring_id, + int cid, int tid); irqreturn_t (*irq_tx)(int irq, void *cookie); /* RX ops */ int (*rx_init)(struct wil6210_priv *wil, u16 ring_size); @@ -821,6 +833,7 @@ extern u8 led_polarity; enum wil6210_vif_status { wil_vif_fwconnecting, wil_vif_fwconnected, + wil_vif_ft_roam, wil_vif_status_last /* keep last */ }; @@ -1204,6 +1217,7 @@ int wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index, int wmi_echo(struct wil6210_priv *wil); int wmi_set_ie(struct wil6210_vif *vif, u8 type, u16 ie_len, const void *ie); int wmi_rx_chain_add(struct wil6210_priv *wil, struct wil_ring *vring); +int wmi_update_ft_ies(struct wil6210_vif *vif, u16 ie_len, const void *ie); int wmi_rxon(struct wil6210_priv *wil, bool on); int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, @@ -1319,6 +1333,9 @@ void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil); void wil_rx_handle(struct wil6210_priv *wil, int *quota); void wil6210_unmask_irq_rx(struct wil6210_priv *wil); void wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil); +void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage, + struct wil_sta_info *cs, + struct key_params *params); int wil_iftype_nl2wmi(enum nl80211_iftype type); @@ -1370,4 +1387,6 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout); +void update_supported_bands(struct wil6210_priv *wil); + #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 42c02a20ec97..c3ad8e4df3ec 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -227,6 +227,14 @@ struct blink_on_off_time led_blink_time[] = { {WIL_LED_BLINK_ON_FAST_MS, WIL_LED_BLINK_OFF_FAST_MS}, }; +struct auth_no_hdr { + __le16 auth_alg; + __le16 auth_transaction; + __le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; +} __packed; + u8 led_polarity = LED_POLARITY_LOW_ACTIVE; /** @@ -468,6 +476,12 @@ static const char *cmdid2name(u16 cmdid) return "WMI_LINK_STATS_CMD"; case WMI_SW_TX_REQ_EXT_CMDID: return "WMI_SW_TX_REQ_EXT_CMDID"; + case WMI_FT_AUTH_CMDID: + return "WMI_FT_AUTH_CMD"; + case WMI_FT_REASSOC_CMDID: + return "WMI_FT_REASSOC_CMD"; + case WMI_UPDATE_FT_IES_CMDID: + return "WMI_UPDATE_FT_IES_CMD"; default: return "Untracked CMD"; } @@ -606,6 +620,12 @@ static const char *eventid2name(u16 eventid) return "WMI_LINK_STATS_CONFIG_DONE_EVENT"; case WMI_LINK_STATS_EVENTID: return "WMI_LINK_STATS_EVENT"; + case WMI_COMMAND_NOT_SUPPORTED_EVENTID: + return "WMI_COMMAND_NOT_SUPPORTED_EVENT"; + case WMI_FT_AUTH_STATUS_EVENTID: + return "WMI_FT_AUTH_STATUS_EVENT"; + case WMI_FT_REASSOC_STATUS_EVENTID: + return "WMI_FT_REASSOC_STATUS_EVENT"; default: return "Untracked EVENT"; } @@ -1156,6 +1176,9 @@ static void wmi_evt_ring_en(struct wil6210_vif *vif, int id, void *d, int len) struct wmi_ring_en_event *evt = d; u8 vri = evt->ring_index; struct wireless_dev *wdev = vif_to_wdev(vif); + struct wil_sta_info *sta; + int cid; + struct key_params params; wil_dbg_wmi(wil, "Enable vring %d MID %d\n", vri, vif->mid); @@ -1164,13 +1187,33 @@ static void wmi_evt_ring_en(struct wil6210_vif *vif, int id, void *d, int len) return; } - if (wdev->iftype != NL80211_IFTYPE_AP || !disable_ap_sme) - /* in AP mode with disable_ap_sme, this is done by - * wil_cfg80211_change_station() + if (wdev->iftype != NL80211_IFTYPE_AP || !disable_ap_sme || + test_bit(wil_vif_ft_roam, vif->status)) + /* in AP mode with disable_ap_sme that is not FT, + * this is done by wil_cfg80211_change_station() */ wil->ring_tx_data[vri].dot1x_open = true; if (vri == vif->bcast_ring) /* no BA for bcast */ return; + + cid = wil->ring2cid_tid[vri][0]; + if (!wil_cid_valid(cid)) { + wil_err(wil, "invalid cid %d for vring %d\n", cid, vri); + return; + } + + /* In FT mode we get key but not store it as it is received + * before WMI_CONNECT_EVENT received from FW. + * wil_set_crypto_rx is called here to reset the security PN + */ + sta = &wil->sta[cid]; + if (test_bit(wil_vif_ft_roam, vif->status)) { + memset(¶ms, 0, sizeof(params)); + wil_set_crypto_rx(0, WMI_KEY_USE_PAIRWISE, sta, ¶ms); + if (wdev->iftype != NL80211_IFTYPE_AP) + clear_bit(wil_vif_ft_roam, vif->status); + } + if (agg_wsize >= 0) wil_addba_tx_request(wil, vri, agg_wsize); } @@ -1462,6 +1505,271 @@ wmi_evt_link_stats(struct wil6210_vif *vif, int id, void *d, int len) } /** + * find cid and ringid for the station vif + * + * return error, if other interfaces are used or ring was not found + */ +static int wil_find_cid_ringid_sta(struct wil6210_priv *wil, + struct wil6210_vif *vif, + int *cid, + int *ringid) +{ + struct wil_ring *ring; + struct wil_ring_tx_data *txdata; + int min_ring_id = wil_get_min_tx_ring_id(wil); + int i; + u8 lcid; + + if (!(vif->wdev.iftype == NL80211_IFTYPE_STATION || + vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) { + wil_err(wil, "invalid interface type %d\n", vif->wdev.iftype); + return -EINVAL; + } + + /* In the STA mode, it is expected to have only one ring + * for the AP we are connected to. + * find it and return the cid associated with it. + */ + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { + ring = &wil->ring_tx[i]; + txdata = &wil->ring_tx_data[i]; + if (!ring->va || !txdata->enabled || txdata->mid != vif->mid) + continue; + + lcid = wil->ring2cid_tid[i][0]; + if (lcid >= WIL6210_MAX_CID) /* skip BCAST */ + continue; + + wil_dbg_wmi(wil, "find sta -> ringid %d cid %d\n", i, lcid); + *cid = lcid; + *ringid = i; + return 0; + } + + wil_dbg_wmi(wil, "find sta cid while no rings active?\n"); + + return -ENOENT; +} + +static void +wmi_evt_auth_status(struct wil6210_vif *vif, int id, void *d, int len) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); + struct wmi_ft_auth_status_event *data = d; + int ie_len = len - offsetof(struct wmi_ft_auth_status_event, ie_info); + int rc, cid = 0, ringid = 0; + struct cfg80211_ft_event_params ft; + u16 d_len; + /* auth_alg(u16) + auth_transaction(u16) + status_code(u16) */ + const size_t auth_ie_offset = sizeof(u16) * 3; + struct auth_no_hdr *auth = (struct auth_no_hdr *)data->ie_info; + + /* check the status */ + if (ie_len >= 0 && data->status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "FT: auth failed. status %d\n", data->status); + goto fail; + } + + if (ie_len < auth_ie_offset) { + wil_err(wil, "FT: auth event too short, len %d\n", len); + goto fail; + } + + d_len = le16_to_cpu(data->ie_len); + if (d_len != ie_len) { + wil_err(wil, + "FT: auth ie length mismatch, d_len %d should be %d\n", + d_len, ie_len); + goto fail; + } + + if (!test_bit(wil_vif_ft_roam, wil->status)) { + wil_err(wil, "FT: Not in roaming state\n"); + goto fail; + } + + if (le16_to_cpu(auth->auth_transaction) != 2) { + wil_err(wil, "FT: auth error. auth_transaction %d\n", + le16_to_cpu(auth->auth_transaction)); + goto fail; + } + + if (le16_to_cpu(auth->auth_alg) != WLAN_AUTH_FT) { + wil_err(wil, "FT: auth error. auth_alg %d\n", + le16_to_cpu(auth->auth_alg)); + goto fail; + } + + wil_dbg_wmi(wil, "FT: Auth to %pM successfully\n", data->mac_addr); + wil_hex_dump_wmi("FT Auth ies : ", DUMP_PREFIX_OFFSET, 16, 1, + data->ie_info, d_len, true); + + /* find cid and ringid */ + rc = wil_find_cid_ringid_sta(wil, vif, &cid, &ringid); + if (rc) { + wil_err(wil, "No valid cid found\n"); + goto fail; + } + + if (vif->privacy) { + /* For secure assoc, remove old keys */ + rc = wmi_del_cipher_key(vif, 0, wil->sta[cid].addr, + WMI_KEY_USE_PAIRWISE); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n"); + goto fail; + } + rc = wmi_del_cipher_key(vif, 0, wil->sta[cid].addr, + WMI_KEY_USE_RX_GROUP); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n"); + goto fail; + } + } + + memset(&ft, 0, sizeof(ft)); + ft.ies = data->ie_info + auth_ie_offset; + ft.ies_len = d_len - auth_ie_offset; + ft.target_ap = data->mac_addr; + cfg80211_ft_event(ndev, &ft); + + return; + +fail: + wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID, false); +} + +static void +wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); + struct wiphy *wiphy = wil_to_wiphy(wil); + struct wmi_ft_reassoc_status_event *data = d; + int ies_len = len - offsetof(struct wmi_ft_reassoc_status_event, + ie_info); + int rc = -ENOENT, cid = 0, ringid = 0; + int ch; /* channel number (primary) */ + size_t assoc_req_ie_len = 0, assoc_resp_ie_len = 0; + u8 *assoc_req_ie = NULL, *assoc_resp_ie = NULL; + /* capinfo(u16) + listen_interval(u16) + current_ap mac addr + IEs */ + const size_t assoc_req_ie_offset = sizeof(u16) * 2 + ETH_ALEN; + /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ + const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + u16 d_len; + int freq; + struct cfg80211_roam_info info; + + if (ies_len < 0) { + wil_err(wil, "ft reassoc event too short, len %d\n", len); + goto fail; + } + + wil_dbg_wmi(wil, "Reasoc Status event: status=%d, aid=%d", + data->status, data->aid); + wil_dbg_wmi(wil, " mac_addr=%pM, beacon_ie_len=%d", + data->mac_addr, data->beacon_ie_len); + wil_dbg_wmi(wil, " reassoc_req_ie_len=%d, reassoc_resp_ie_len=%d", + le16_to_cpu(data->reassoc_req_ie_len), + le16_to_cpu(data->reassoc_resp_ie_len)); + + d_len = le16_to_cpu(data->beacon_ie_len) + + le16_to_cpu(data->reassoc_req_ie_len) + + le16_to_cpu(data->reassoc_resp_ie_len); + if (d_len != ies_len) { + wil_err(wil, + "ft reassoc ie length mismatch, d_len %d should be %d\n", + d_len, ies_len); + goto fail; + } + + /* check the status */ + if (data->status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "ft reassoc failed. status %d\n", data->status); + goto fail; + } + + /* find cid and ringid */ + rc = wil_find_cid_ringid_sta(wil, vif, &cid, &ringid); + if (rc) { + wil_err(wil, "No valid cid found\n"); + goto fail; + } + + ch = data->channel + 1; + wil_info(wil, "FT: Roam %pM channel [%d] cid %d aid %d\n", + data->mac_addr, ch, cid, data->aid); + + wil_hex_dump_wmi("reassoc AI : ", DUMP_PREFIX_OFFSET, 16, 1, + data->ie_info, len - sizeof(*data), true); + + /* figure out IE's */ + if (le16_to_cpu(data->reassoc_req_ie_len) > assoc_req_ie_offset) { + assoc_req_ie = &data->ie_info[assoc_req_ie_offset]; + assoc_req_ie_len = le16_to_cpu(data->reassoc_req_ie_len) - + assoc_req_ie_offset; + } + if (le16_to_cpu(data->reassoc_resp_ie_len) <= assoc_resp_ie_offset) { + wil_err(wil, "FT: reassoc resp ie len is too short, len %d\n", + le16_to_cpu(data->reassoc_resp_ie_len)); + goto fail; + } + + assoc_resp_ie = &data->ie_info[le16_to_cpu(data->reassoc_req_ie_len) + + assoc_resp_ie_offset]; + assoc_resp_ie_len = le16_to_cpu(data->reassoc_resp_ie_len) - + assoc_resp_ie_offset; + + if (test_bit(wil_status_resetting, wil->status) || + !test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "FT: status_resetting, cancel reassoc event\n"); + /* no need for cleanup, wil_reset will do that */ + return; + } + + mutex_lock(&wil->mutex); + + /* ring modify to set the ring for the roamed AP settings */ + wil_dbg_wmi(wil, + "ft modify tx config for connection CID %d ring %d\n", + cid, ringid); + + rc = wil->txrx_ops.tx_ring_modify(vif, ringid, cid, 0); + if (rc) { + wil_err(wil, "modify TX for CID %d MID %d ring %d failed (%d)\n", + cid, vif->mid, ringid, rc); + mutex_unlock(&wil->mutex); + goto fail; + } + + /* Update the driver STA members with the new bss */ + wil->sta[cid].aid = data->aid; + wil->sta[cid].stats.ft_roams++; + ether_addr_copy(wil->sta[cid].addr, vif->bss->bssid); + mutex_unlock(&wil->mutex); + del_timer_sync(&vif->connect_timer); + + cfg80211_ref_bss(wiphy, vif->bss); + freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); + + memset(&info, 0, sizeof(info)); + info.channel = ieee80211_get_channel(wiphy, freq); + info.bss = vif->bss; + info.req_ie = assoc_req_ie; + info.req_ie_len = assoc_req_ie_len; + info.resp_ie = assoc_resp_ie; + info.resp_ie_len = assoc_resp_ie_len; + cfg80211_roamed(ndev, &info, GFP_KERNEL); + vif->bss = NULL; + + return; + +fail: + wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID, false); +} + +/** * Some events are ignored for purpose; and need not be interpreted as * "unhandled events" */ @@ -1492,6 +1800,8 @@ static const struct { {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore}, {WMI_SCHED_SCAN_RESULT_EVENTID, wmi_evt_sched_scan_result}, {WMI_LINK_STATS_EVENTID, wmi_evt_link_stats}, + {WMI_FT_AUTH_STATUS_EVENTID, wmi_evt_auth_status}, + {WMI_FT_REASSOC_STATUS_EVENTID, wmi_evt_reassoc_status}, }; /* @@ -2086,6 +2396,40 @@ out: return rc; } +int wmi_update_ft_ies(struct wil6210_vif *vif, u16 ie_len, const void *ie) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + u16 len; + struct wmi_update_ft_ies_cmd *cmd; + int rc; + + if (!ie) + ie_len = 0; + + len = sizeof(struct wmi_update_ft_ies_cmd) + ie_len; + if (len < ie_len) { + wil_err(wil, "wraparound. ie len %d\n", ie_len); + return -EINVAL; + } + + cmd = kzalloc(len, GFP_KERNEL); + if (!cmd) { + rc = -ENOMEM; + goto out; + } + + cmd->ie_len = cpu_to_le16(ie_len); + memcpy(cmd->ie_info, ie, ie_len); + rc = wmi_send(wil, WMI_UPDATE_FT_IES_CMDID, vif->mid, cmd, len); + kfree(cmd); + +out: + if (rc) + wil_err(wil, "update ft ies failed : %d\n", rc); + + return rc; +} + /** * wmi_rxon - turn radio on/off * @on: turn on if true, off otherwise diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 139acb2caf92..b668758da994 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -103,6 +103,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_AMSDU = 23, WMI_FW_CAPABILITY_RAW_MODE = 24, WMI_FW_CAPABILITY_TX_REQ_EXT = 25, + WMI_FW_CAPABILITY_CHANNEL_4 = 26, WMI_FW_CAPABILITY_MAX, }; @@ -2369,6 +2370,7 @@ struct wmi_ft_reassoc_status_event { __le16 beacon_ie_len; __le16 reassoc_req_ie_len; __le16 reassoc_resp_ie_len; + u8 reserved[4]; u8 ie_info[0]; } __packed; diff --git a/drivers/net/wireless/broadcom/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h index b77d1a904f7e..9fc7c088a539 100644 --- a/drivers/net/wireless/broadcom/b43/b43.h +++ b/drivers/net/wireless/broadcom/b43/b43.h @@ -909,7 +909,7 @@ struct b43_wl { /* Set this if we call ieee80211_register_hw() and check if we call * ieee80211_unregister_hw(). */ - bool hw_registred; + bool hw_registered; /* We can only have one operating interface (802.11 core) * at a time. General information about this interface follows. diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c index 6b0e1ec346cb..d113bd997f4b 100644 --- a/drivers/net/wireless/broadcom/b43/dma.c +++ b/drivers/net/wireless/broadcom/b43/dma.c @@ -1432,7 +1432,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) goto out; } - if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) { + if (WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME)) { /* If we get here, we have a real error with the queue * full, but queues not stopped. */ b43err(dev->wl, "DMA queue overflow\n"); diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index b37e7391f55d..4daa1ce8cba3 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -2611,7 +2611,7 @@ start_ieee80211: err = ieee80211_register_hw(wl->hw); if (err) goto err_one_core_detach; - wl->hw_registred = true; + wl->hw_registered = true; b43_leds_register(wl->current_dev); /* Register HW RNG driver */ @@ -5610,7 +5610,7 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); - wl->hw_registred = false; + wl->hw_registered = false; hw->max_rates = 2; SET_IEEE80211_DEV(hw, dev->dev); if (is_valid_ether_addr(sprom->et1mac)) @@ -5693,7 +5693,7 @@ static void b43_bcma_remove(struct bcma_device *core) B43_WARN_ON(!wl); if (!wldev->fw.ucode.data) return; /* NULL if firmware never loaded */ - if (wl->current_dev == wldev && wl->hw_registred) { + if (wl->current_dev == wldev && wl->hw_registered) { b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } @@ -5776,7 +5776,7 @@ static void b43_ssb_remove(struct ssb_device *sdev) B43_WARN_ON(!wl); if (!wldev->fw.ucode.data) return; /* NULL if firmware never loaded */ - if (wl->current_dev == wldev && wl->hw_registred) { + if (wl->current_dev == wldev && wl->hw_registered) { b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } diff --git a/drivers/net/wireless/broadcom/b43legacy/dma.c b/drivers/net/wireless/broadcom/b43legacy/dma.c index 2f0c64cef65f..1b1da7d83652 100644 --- a/drivers/net/wireless/broadcom/b43legacy/dma.c +++ b/drivers/net/wireless/broadcom/b43legacy/dma.c @@ -1149,7 +1149,7 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, return -ENOSPC; } - if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { + if (WARN_ON(free_slots(ring) < SLOTS_PER_PACKET)) { /* If we get here, we have a real error with the queue * full, but queues not stopped. */ b43legacyerr(dev->wl, "DMA queue overflow\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 5444e6213d45..230a378c26fc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1649,6 +1649,14 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) case WLAN_AKM_SUITE_PSK: val = WPA2_AUTH_PSK; break; + case WLAN_AKM_SUITE_FT_8021X: + val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT; + if (sme->want_1x) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + break; + case WLAN_AKM_SUITE_FT_PSK: + val = WPA2_AUTH_PSK | WPA2_AUTH_FT; + break; default: brcmf_err("invalid cipher group (%d)\n", sme->crypto.cipher_group); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index cd3651069d0c..94044a7a6021 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -296,9 +296,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) /* Replace all newline/linefeed characters with space * character */ - ptr = clmver; - while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL) - *ptr = ' '; + strreplace(clmver, '\n', ' '); brcmf_dbg(INFO, "CLM version = %s\n", clmver); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 8347da632a5b..4c5a3995dc35 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -178,7 +178,7 @@ static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp, ifp->fwil_fwerr = false; } -#define MAX_CAPS_BUFFER_SIZE 512 +#define MAX_CAPS_BUFFER_SIZE 768 static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp) { char caps[MAX_CAPS_BUFFER_SIZE]; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 4fffa6988087..5dea569d63ed 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2017,6 +2017,7 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), + BRCMF_PCIE_DEVICE_SUB(0x4355, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4355), BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 1e2fd289323a..b2e1ab5adb64 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1463,7 +1463,7 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) struct sk_buff *pfirst, *pnext; int errcode; - u8 doff, sfdoff; + u8 doff; struct brcmf_sdio_hdrinfo rd_new; @@ -1597,7 +1597,6 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) /* Remove superframe header, remember offset */ skb_pull(pfirst, rd_new.dat_offset); - sfdoff = rd_new.dat_offset; num = 0; /* Validate all the subframe headers */ @@ -3405,7 +3404,6 @@ static int brcmf_sdio_bus_preinit(struct device *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; @@ -3448,7 +3446,6 @@ static int brcmf_sdio_bus_preinit(struct device *dev) if (sdiodev->sg_support) { bus->txglom = false; value = 1; - pad_size = bus->sdiodev->func2->cur_blksize << 1; err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", &value, sizeof(u32)); if (err < 0) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c index 2fe1f6863278..3bd54f125776 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c @@ -62,8 +62,7 @@ int brcms_debugfs_attach(struct brcms_pub *drvr) void brcms_debugfs_detach(struct brcms_pub *drvr) { - if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) - debugfs_remove_recursive(drvr->dbgfs_dir); + debugfs_remove_recursive(drvr->dbgfs_dir); } struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index ecc89e718b9c..6255fb6d97a7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -1578,10 +1578,10 @@ int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx) if (le32_to_cpu(hdr->idx) == idx) { pdata = wl->fw.fw_bin[i]->data + le32_to_cpu(hdr->offset); - *pbuf = kmemdup(pdata, len, GFP_KERNEL); + *pbuf = kvmalloc(len, GFP_KERNEL); if (*pbuf == NULL) goto fail; - + memcpy(*pbuf, pdata, len); return 0; } } @@ -1629,7 +1629,7 @@ int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx) */ void brcms_ucode_free_buf(void *p) { - kfree(p); + kvfree(p); } /* diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c index bedec1606caa..a57f2711f3c0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c @@ -25453,12 +25453,12 @@ void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype) (pi->cal_type_override == PHY_PERICAL_FULL) ? true : false; - if ((pi->mphase_cal_phase_id > MPHASE_CAL_STATE_INIT)) { + if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_INIT) { if (pi->nphy_txiqlocal_chanspec != pi->radio_chanspec) wlc_phy_cal_perical_mphase_restart(pi); } - if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL)) + if (pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL) wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); wlapi_suspend_mac_and_wait(pi->sh->physhim); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c index d8b79cb72b58..e7584b842dce 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c @@ -77,6 +77,8 @@ static u16 d11ac_bw(enum brcmu_chan_bw bw) return BRCMU_CHSPEC_D11AC_BW_40; case BRCMU_CHAN_BW_80: return BRCMU_CHSPEC_D11AC_BW_80; + case BRCMU_CHAN_BW_160: + return BRCMU_CHSPEC_D11AC_BW_160; default: WARN_ON(1); } @@ -190,8 +192,38 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) break; } break; - case BRCMU_CHSPEC_D11AC_BW_8080: case BRCMU_CHSPEC_D11AC_BW_160: + switch (ch->sb) { + case BRCMU_CHAN_SB_LLL: + ch->control_ch_num -= CH_70MHZ_APART; + break; + case BRCMU_CHAN_SB_LLU: + ch->control_ch_num -= CH_50MHZ_APART; + break; + case BRCMU_CHAN_SB_LUL: + ch->control_ch_num -= CH_30MHZ_APART; + break; + case BRCMU_CHAN_SB_LUU: + ch->control_ch_num -= CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_ULL: + ch->control_ch_num += CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_ULU: + ch->control_ch_num += CH_30MHZ_APART; + break; + case BRCMU_CHAN_SB_UUL: + ch->control_ch_num += CH_50MHZ_APART; + break; + case BRCMU_CHAN_SB_UUU: + ch->control_ch_num += CH_70MHZ_APART; + break; + default: + WARN_ON_ONCE(1); + break; + } + break; + case BRCMU_CHSPEC_D11AC_BW_8080: default: WARN_ON_ONCE(1); break; diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h index 7b9a77981df1..dddebaa60352 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h @@ -29,6 +29,8 @@ #define CH_UPPER_SB 0x01 #define CH_LOWER_SB 0x02 #define CH_EWA_VALID 0x04 +#define CH_70MHZ_APART 14 +#define CH_50MHZ_APART 10 #define CH_30MHZ_APART 6 #define CH_20MHZ_APART 4 #define CH_10MHZ_APART 2 @@ -237,6 +239,7 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WPA2_AUTH_RESERVED4 0x0400 #define WPA2_AUTH_RESERVED5 0x0800 #define WPA2_AUTH_1X_SHA256 0x1000 /* 1X with SHA256 key derivation */ +#define WPA2_AUTH_FT 0x4000 /* Fast BSS Transition */ #define WPA2_AUTH_PSK_SHA256 0x8000 /* PSK with SHA256 key derivation */ #define DOT11_DEFAULT_RTS_LEN 2347 diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 9644e7b93645..bbdca13c5a9f 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -5652,7 +5652,7 @@ static void ipw_merge_adhoc_network(struct work_struct *work) } mutex_lock(&priv->mutex); - if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) { + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { IPW_DEBUG_MERGE("remove network %*pE\n", priv->essid_len, priv->essid); ipw_remove_current_network(priv); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c index 591687984962..46686ee88ff4 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c @@ -12,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c index fedb108db68f..e7e45846dd07 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c @@ -12,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 91ca77c7571c..a8acc755a02c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -77,10 +77,13 @@ #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_A_F0_FW_PRE "iwlwifi-QuQnj-f0-hr-a0-" -#define IWL_22000_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0-" +#define IWL_22000_HR_B_F0_FW_PRE "iwlwifi-Qu-b0-hr-b0-" +#define IWL_22000_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0-" +#define IWL_22000_HR_B_FW_PRE "iwlwifi-QuQnj-b0-hr-b0-" #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_SU_Z0_FW_PRE "iwlwifi-su-z0-" +#define IWL_QU_B_JF_B_FW_PRE "iwlwifi-Qu-b0-jf-b0-" #define IWL_22000_HR_MODULE_FIRMWARE(api) \ IWL_22000_HR_FW_PRE __stringify(api) ".ucode" @@ -88,7 +91,11 @@ IWL_22000_JF_FW_PRE __stringify(api) ".ucode" #define IWL_22000_HR_A_F0_QNJ_MODULE_FIRMWARE(api) \ IWL_22000_HR_A_F0_FW_PRE __stringify(api) ".ucode" -#define IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(api) \ +#define IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(api) \ + IWL_22000_HR_B_F0_FW_PRE __stringify(api) ".ucode" +#define IWL_22000_QU_B_HR_B_MODULE_FIRMWARE(api) \ + IWL_22000_QU_B_HR_B_FW_PRE __stringify(api) ".ucode" +#define IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(api) \ IWL_22000_HR_B_FW_PRE __stringify(api) ".ucode" #define IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(api) \ IWL_22000_JF_B0_FW_PRE __stringify(api) ".ucode" @@ -96,6 +103,8 @@ IWL_22000_HR_A0_FW_PRE __stringify(api) ".ucode" #define IWL_22000_SU_Z0_MODULE_FIRMWARE(api) \ IWL_22000_SU_Z0_FW_PRE __stringify(api) ".ucode" +#define IWL_QU_B_JF_B_MODULE_FIRMWARE(api) \ + IWL_QU_B_JF_B_FW_PRE __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_22000 10 @@ -134,7 +143,7 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .ucode_api_min = IWL_22000_UCODE_API_MIN, \ .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_22000, \ - .non_shared_ant = ANT_A, \ + .non_shared_ant = ANT_B, \ .dccm_offset = IWL_22000_DCCM_OFFSET, \ .dccm_len = IWL_22000_DCCM_LEN, \ .dccm2_offset = IWL_22000_DCCM2_OFFSET, \ @@ -155,7 +164,9 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .gen2 = true, \ .nvm_type = IWL_NVM_EXT, \ .dbgc_supported = true, \ - .min_umac_error_event_table = 0x400000 + .min_umac_error_event_table = 0x400000, \ + .d3_debug_data_base_addr = 0x401000, \ + .d3_debug_data_length = 60 * 1024 #define IWL_DEVICE_22500 \ IWL_DEVICE_22000_COMMON, \ @@ -190,7 +201,54 @@ const struct iwl_cfg iwl22000_2ac_cfg_jf = { const struct iwl_cfg iwl22000_2ax_cfg_hr = { .name = "Intel(R) Dual Band Wireless AX 22000", - .fw_name_pre = IWL_22000_HR_FW_PRE, + .fw_name_pre = IWL_22000_QU_B_HR_B_FW_PRE, + IWL_DEVICE_22500, + /* + * This device doesn't support receiving BlockAck with a large bitmap + * so we need to restrict the size of transmitted aggregation to the + * HT size; mac80211 would otherwise pick the HE max (256) by default. + */ + .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, +}; + +/* + * All JF radio modules are part of the 9000 series, but the MAC part + * looks more like 22000. That's why this device is here, but called + * 9560 nevertheless. + */ +const struct iwl_cfg iwl9461_2ac_cfg_qu_b0_jf_b0 = { + .name = "Intel(R) Wireless-AC 9461", + .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, + IWL_DEVICE_22500, +}; + +const struct iwl_cfg iwl9462_2ac_cfg_qu_b0_jf_b0 = { + .name = "Intel(R) Wireless-AC 9462", + .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, + IWL_DEVICE_22500, +}; + +const struct iwl_cfg iwl9560_2ac_cfg_qu_b0_jf_b0 = { + .name = "Intel(R) Wireless-AC 9560", + .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, + IWL_DEVICE_22500, +}; + +const struct iwl_cfg killer1550i_2ac_cfg_qu_b0_jf_b0 = { + .name = "Killer (R) Wireless-AC 1550i Wireless Network Adapter (9560NGW)", + .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, + IWL_DEVICE_22500, +}; + +const struct iwl_cfg killer1550s_2ac_cfg_qu_b0_jf_b0 = { + .name = "Killer (R) Wireless-AC 1550s Wireless Network Adapter (9560NGW)", + .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, + IWL_DEVICE_22500, +}; + +const struct iwl_cfg iwl22000_2ax_cfg_jf = { + .name = "Intel(R) Dual Band Wireless AX 22000", + .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, IWL_DEVICE_22500, /* * This device doesn't support receiving BlockAck with a large bitmap @@ -264,7 +322,10 @@ const struct iwl_cfg iwl22560_2ax_cfg_su_cdb = { 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_A_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_22000_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_HR_B_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)); MODULE_FIRMWARE(IWL_22000_SU_Z0_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_QU_B_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c index 36151e61a26f..575a7022d045 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c @@ -12,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c index b5d8274761d8..30e62a7c9d52 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c @@ -12,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c index a62c8346f13a..c973bfaa3414 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c index c46fa712985b..348c40fcddcb 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index 24b2f7cbb308..37deaf4fd7b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -155,7 +155,9 @@ static const struct iwl_tt_params iwl9000_tt_params = { .nvm_type = IWL_NVM_EXT, \ .dbgc_supported = true, \ .min_umac_error_event_table = 0x800000, \ - .csr = &iwl_csr_v1 + .csr = &iwl_csr_v1, \ + .d3_debug_data_base_addr = 0x401000, \ + .d3_debug_data_length = 92 * 1024 const struct iwl_cfg iwl9160_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9160", diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h index b79e38734f2f..431e13c6ee35 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c index c96f9b1d948a..588b15697710 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.h b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h index 099e3ce80ffc..c43ba94bfa8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/calib.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h index f89736d60a3d..0f4be4be181c 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c index 096a07c5a33f..3d2e44a642de 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c @@ -13,11 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h index cceb4cd8e501..c5b8376d827f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c index f21732ec3b25..3dd7d8c45dab 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c index 1bbd17ada974..04c236e9399b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.h b/drivers/net/wireless/intel/iwlwifi/dvm/led.h index 75f74edd018f..8f93a3246dee 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index 2b6ffbc46fa5..b2f172d4f78a 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -13,11 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 82caae02dd09..49b71dbf8490 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 030482b357a3..1088ff036e13 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -2,6 +2,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -15,10 +16,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -1651,7 +1648,6 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv) priv->status, table.valid); } - trace_iwlwifi_dev_ucode_error(trans->dev, &table, 0, table.brd_ver); IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); IWL_ERR(priv, "0x%08X | uPc\n", table.pc); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c index 0ad557c89514..8c25e3aefb2b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.h b/drivers/net/wireless/intel/iwlwifi/dvm/power.h index 2fd9b43adafd..a04fd4d375c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/power.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.h @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c index 98050d7be411..ef4b9de256f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.h b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h index 50c1e951dd2d..b2df3a8cc464 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index c942830af2b5..6f17a5e24e82 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -15,10 +15,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c index 8f3e5586eda9..eee1d48d453a 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c @@ -12,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c index 17e6a32384d3..8d7aafb4d9e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c @@ -13,11 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c index de6ec9b7ace4..b1792de09594 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c index 6524533d723c..4de2727ac63e 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h index d324e9be9cbf..6388c09603c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index fb40ddfced99..4ff323a3a4e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -13,11 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c index d6013bfe991c..3bf57085b976 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c @@ -14,11 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 75cae54ea7de..32d000cffe9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -16,9 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index cb5f32c1d705..2439e98431ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -16,9 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index 87c1ddea75ae..68060085010f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -8,6 +8,7 @@ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -30,6 +31,7 @@ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -203,6 +205,7 @@ enum iwl_bt_activity_grading { BT_ON_NO_CONNECTION = 1, BT_LOW_TRAFFIC = 2, BT_HIGH_TRAFFIC = 3, + BT_VERY_HIGH_TRAFFIC = 4, BT_MAX_AG, }; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 6dad748e5cdc..8b4922bbe139 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -436,7 +436,8 @@ enum iwl_legacy_cmds { /** * @REDUCE_TX_POWER_CMD: - * &struct iwl_dev_tx_power_cmd_v3 or &struct iwl_dev_tx_power_cmd + * &struct iwl_dev_tx_power_cmd_v3 or &struct iwl_dev_tx_power_cmd_v4 + * or &struct iwl_dev_tx_power_cmd */ REDUCE_TX_POWER_CMD = 0x9f, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index 57f4bc242023..6fae02fa4cad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -30,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -374,7 +376,7 @@ enum iwl_wowlan_wakeup_reason { }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ -struct iwl_wowlan_gtk_status { +struct iwl_wowlan_gtk_status_v1 { u8 key_index; u8 reserved[3]; u8 decrypt_key[16]; @@ -382,9 +384,84 @@ struct iwl_wowlan_gtk_status { struct iwl_wowlan_rsc_tsc_params_cmd rsc; } __packed; /* WOWLAN_GTK_MATERIAL_VER_1 */ +#define WOWLAN_KEY_MAX_SIZE 32 +#define WOWLAN_GTK_KEYS_NUM 2 +#define WOWLAN_IGTK_KEYS_NUM 2 + +/** + * struct iwl_wowlan_gtk_status - GTK status + * @key: GTK material + * @key_len: GTK legth, if set to 0, the key is not available + * @key_flags: information about the key: + * bits[0:1]: key index assigned by the AP + * bits[2:6]: GTK index of the key in the internal DB + * bit[7]: Set iff this is the currently used GTK + * @reserved: padding + * @tkip_mic_key: TKIP RX MIC key + * @rsc: TSC RSC counters + */ +struct iwl_wowlan_gtk_status { + u8 key[WOWLAN_KEY_MAX_SIZE]; + u8 key_len; + u8 key_flags; + u8 reserved[2]; + u8 tkip_mic_key[8]; + struct iwl_wowlan_rsc_tsc_params_cmd rsc; +} __packed; /* WOWLAN_GTK_MATERIAL_VER_2 */ + +#define IWL_WOWLAN_GTK_IDX_MASK (BIT(0) | BIT(1)) + +/** + * struct iwl_wowlan_igtk_status - IGTK status + * @key: IGTK material + * @ipn: the IGTK packet number (replay counter) + * @key_len: IGTK length, if set to 0, the key is not available + * @key_flags: information about the key: + * bits[0]: key index assigned by the AP (0: index 4, 1: index 5) + * bits[1:5]: IGTK index of the key in the internal DB + * bit[6]: Set iff this is the currently used IGTK + */ +struct iwl_wowlan_igtk_status { + u8 key[WOWLAN_KEY_MAX_SIZE]; + u8 ipn[6]; + u8 key_len; + u8 key_flags; +} __packed; /* WOWLAN_IGTK_MATERIAL_VER_1 */ + +/** + * struct iwl_wowlan_status_v6 - WoWLAN status + * @gtk: GTK data + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched pattern + * @non_qos_seq_ctr: non-QoS sequence counter to use next + * @qos_seq_ctr: QoS sequence counters to use next + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @num_of_gtk_rekeys: number of GTK rekeys + * @transmitted_ndps: number of transmitted neighbor discovery packets + * @received_beacons: number of received beacons + * @wake_packet_length: wakeup packet length + * @wake_packet_bufsize: wakeup packet buffer size + * @wake_packet: wakeup packet + */ +struct iwl_wowlan_status_v6 { + struct iwl_wowlan_gtk_status_v1 gtk; + __le64 replay_ctr; + __le16 pattern_number; + __le16 non_qos_seq_ctr; + __le16 qos_seq_ctr[8]; + __le32 wakeup_reasons; + __le32 num_of_gtk_rekeys; + __le32 transmitted_ndps; + __le32 received_beacons; + __le32 wake_packet_length; + __le32 wake_packet_bufsize; + u8 wake_packet[]; /* can be truncated from _length to _bufsize */ +} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ + /** * struct iwl_wowlan_status - WoWLAN status * @gtk: GTK data + * @igtk: IGTK data * @replay_ctr: GTK rekey replay counter * @pattern_number: number of the matched pattern * @non_qos_seq_ctr: non-QoS sequence counter to use next @@ -398,7 +475,8 @@ struct iwl_wowlan_gtk_status { * @wake_packet: wakeup packet */ struct iwl_wowlan_status { - struct iwl_wowlan_gtk_status gtk; + struct iwl_wowlan_gtk_status gtk[WOWLAN_GTK_KEYS_NUM]; + struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM]; __le64 replay_ctr; __le16 pattern_number; __le16 non_qos_seq_ctr; @@ -410,7 +488,12 @@ struct iwl_wowlan_status { __le32 wake_packet_length; __le32 wake_packet_bufsize; u8 wake_packet[]; /* can be truncated from _length to _bufsize */ -} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ +} __packed; /* WOWLAN_STATUSES_API_S_VER_7 */ + +static inline u8 iwlmvm_wowlan_gtk_idx(struct iwl_wowlan_gtk_status *gtk) +{ + return gtk->key_flags & IWL_WOWLAN_GTK_IDX_MASK; +} #define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 #define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 59b3c6e8f37b..eff3249af48a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -100,6 +100,11 @@ enum iwl_data_path_subcmd_ids { TLC_MNG_CONFIG_CMD = 0xF, /** + * @HE_AIR_SNIFFER_CONFIG_CMD: &struct iwl_he_monitor_cmd + */ + HE_AIR_SNIFFER_CONFIG_CMD = 0x13, + + /** * @TLC_MNG_UPDATE_NOTIF: &struct iwl_tlc_update_notif */ TLC_MNG_UPDATE_NOTIF = 0xF7, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 106782341544..dc1fa377087a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -8,6 +8,7 @@ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * 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 @@ -30,6 +31,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -336,6 +338,9 @@ struct iwl_dbg_mem_access_rsp { #define CONT_REC_COMMAND_SIZE 80 #define ENABLE_CONT_RECORDING 0x15 #define DISABLE_CONT_RECORDING 0x16 +#define BUFFER_ALLOCATION 0x27 +#define START_DEBUG_RECORDING 0x29 +#define STOP_DEBUG_RECORDING 0x2A /* * struct iwl_continuous_record_mode - recording mode @@ -353,4 +358,31 @@ struct iwl_continuous_record_cmd { sizeof(struct iwl_continuous_record_mode)]; } __packed; +/* maximum fragments to be allocated per target of allocationId */ +#define IWL_BUFFER_LOCATION_MAX_FRAGS 2 + +/** + * struct iwl_fragment_data single fragment structure + * @address: 64bit start address + * @size: size in bytes + */ +struct iwl_fragment_data { + __le64 address; + __le32 size; +} __packed; /* FRAGMENT_STRUCTURE_API_S_VER_1 */ + +/** + * struct iwl_buffer_allocation_cmd - buffer allocation command structure + * @allocation_id: id of the allocation + * @buffer_location: location of the buffer + * @num_frags: number of fragments + * @fragments: memory fragments + */ +struct iwl_buffer_allocation_cmd { + __le32 allocation_id; + __le32 buffer_location; + __le32 num_frags; + struct iwl_fragment_data fragments[IWL_BUFFER_LOCATION_MAX_FRAGS]; +} __packed; /* BUFFER_ALLOCATION_CMD_API_S_VER_1 */ + #endif /* __iwl_fw_api_debug_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index 17c7ef1662a9..ca49db786ed6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -30,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -72,11 +74,58 @@ enum iwl_mac_conf_subcmd_ids { */ LOW_LATENCY_CMD = 0x3, /** + * @PROBE_RESPONSE_DATA_NOTIF: &struct iwl_probe_resp_data_notif + */ + PROBE_RESPONSE_DATA_NOTIF = 0xFC, + + /** * @CHANNEL_SWITCH_NOA_NOTIF: &struct iwl_channel_switch_noa_notif */ CHANNEL_SWITCH_NOA_NOTIF = 0xFF, }; +#define IWL_P2P_NOA_DESC_COUNT (2) + +/** + * struct iwl_p2p_noa_attr - NOA attr contained in probe resp FW notification + * + * @id: attribute id + * @len_low: length low half + * @len_high: length high half + * @idx: instance of NoA timing + * @ctwin: GO's ct window and pwer save capability + * @desc: NoA descriptor + * @reserved: reserved for alignment purposes + */ +struct iwl_p2p_noa_attr { + u8 id; + u8 len_low; + u8 len_high; + u8 idx; + u8 ctwin; + struct ieee80211_p2p_noa_desc desc[IWL_P2P_NOA_DESC_COUNT]; + u8 reserved; +} __packed; + +#define IWL_PROBE_RESP_DATA_NO_CSA (0xff) + +/** + * struct iwl_probe_resp_data_notif - notification with NOA and CSA counter + * + * @mac_id: the mac which should send the probe response + * @noa_active: notifies if the noa attribute should be handled + * @noa_attr: P2P NOA attribute + * @csa_counter: current csa counter + * @reserved: reserved for alignment purposes + */ +struct iwl_probe_resp_data_notif { + __le32 mac_id; + __le32 noa_active; + struct iwl_p2p_noa_attr noa_attr; + u8 csa_counter; + u8 reserved[3]; +} __packed; /* PROBE_RESPONSE_DATA_NTFY_API_S_VER_1 */ + /** * struct iwl_channel_switch_noa_notif - Channel switch NOA notification * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 55594c93b014..1dd23f846fb9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -578,4 +578,18 @@ struct iwl_he_sta_context_cmd { struct iwl_he_backoff_conf trig_based_txf[AC_NUM]; } __packed; /* STA_CONTEXT_DOT11AX_API_S */ +/** + * struct iwl_he_monitor_cmd - configure air sniffer for HE + * @bssid: the BSSID to sniff for + * @reserved1: reserved for dword alignment + * @aid: the AID to track on for HE MU + * @reserved2: reserved for future use + */ +struct iwl_he_monitor_cmd { + u8 bssid[6]; + __le16 reserved1; + __le16 aid; + u8 reserved2[6]; +} __packed; /* HE_AIR_SNIFFER_CONFIG_CMD_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index a3c77e01863b..286a22da232d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * 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 @@ -30,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -316,7 +318,9 @@ enum iwl_dev_tx_power_cmd_mode { IWL_TX_POWER_MODE_SET_DEVICE = 1, IWL_TX_POWER_MODE_SET_CHAINS = 2, IWL_TX_POWER_MODE_SET_ACK = 3, -}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_4 */; + IWL_TX_POWER_MODE_SET_SAR_TIMER = 4, + IWL_TX_POWER_MODE_SET_SAR_TIMER_DEFAULT_TABLE = 5, +}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_5 */; #define IWL_NUM_CHAIN_LIMITS 2 #define IWL_NUM_SUB_BANDS 5 @@ -350,13 +354,35 @@ struct iwl_dev_tx_power_cmd_v3 { * reduction. * @reserved: reserved (padding) */ -struct iwl_dev_tx_power_cmd { +struct iwl_dev_tx_power_cmd_v4 { /* v4 is just an extension of v3 - keep this here */ struct iwl_dev_tx_power_cmd_v3 v3; u8 enable_ack_reduction; u8 reserved[3]; } __packed; /* TX_REDUCED_POWER_API_S_VER_4 */ +/** + * struct iwl_dev_tx_power_cmd - TX power reduction command + * @v3: version 3 of the command, embedded here for easier software handling + * @enable_ack_reduction: enable or disable close range ack TX power + * reduction. + * @per_chain_restriction_changed: is per_chain_restriction has changed + * from last command. used if set_mode is + * IWL_TX_POWER_MODE_SET_SAR_TIMER. + * note: if not changed, the command is used for keep alive only. + * @reserved: reserved (padding) + * @timer_period: timer in milliseconds. if expires FW will change to default + * BIOS values. relevant if setMode is IWL_TX_POWER_MODE_SET_SAR_TIMER + */ +struct iwl_dev_tx_power_cmd { + /* v5 is just an extension of v3 - keep this here */ + struct iwl_dev_tx_power_cmd_v3 v3; + u8 enable_ack_reduction; + u8 per_chain_restriction_changed; + u8 reserved[2]; + __le32 timer_period; +} __packed; /* TX_REDUCED_POWER_API_S_VER_5 */ + #define IWL_NUM_GEO_PROFILES 3 /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 087fae91baef..9eddc4dc2ae6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -66,12 +66,24 @@ /** * enum iwl_tlc_mng_cfg_flags_enum - options for TLC config flags - * @IWL_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC + * @IWL_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC. For HE this enables STBC for + * bandwidths <= 80MHz * @IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK: enable LDPC + * @IWL_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK: enable STBC in HE at 160MHz + * bandwidth + * @IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK: enable HE Dual Carrier Modulation + * for BPSK (MCS 0) with 1 spatial + * stream + * @IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK: enable HE Dual Carrier Modulation + * for BPSK (MCS 0) with 2 spatial + * streams */ enum iwl_tlc_mng_cfg_flags { - IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(0), - IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK = BIT(1), + IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(0), + IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK = BIT(1), + IWL_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK = BIT(2), + IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK = BIT(3), + IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK = BIT(4), }; /** @@ -217,66 +229,6 @@ struct iwl_tlc_update_notif { __le32 amsdu_enabled; } __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */ -/** - * 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]; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 2f599353c885..415b8842b426 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -362,18 +362,49 @@ enum iwl_rx_he_phy { /* 6 bits reserved */ IWL_RX_HE_PHY_DELIM_EOF = BIT(31), - /* second dword - MU data */ - IWL_RX_HE_PHY_SIGB_COMPRESSION = BIT_ULL(32 + 0), - IWL_RX_HE_PHY_SIBG_SYM_OR_USER_NUM_MASK = 0x1e00000000ULL, + /* second dword - common data */ IWL_RX_HE_PHY_HE_LTF_NUM_MASK = 0xe000000000ULL, IWL_RX_HE_PHY_RU_ALLOC_SEC80 = BIT_ULL(32 + 8), /* trigger encoded */ IWL_RX_HE_PHY_RU_ALLOC_MASK = 0xfe0000000000ULL, - IWL_RX_HE_PHY_SIGB_MCS_MASK = 0xf000000000000ULL, - /* 1 bit reserved */ - IWL_RX_HE_PHY_SIGB_DCM = BIT_ULL(32 + 21), - IWL_RX_HE_PHY_PREAMBLE_PUNC_TYPE_MASK = 0xc0000000000000ULL, - /* 8 bits reserved */ + IWL_RX_HE_PHY_INFO_TYPE_MASK = 0xf000000000000000ULL, + IWL_RX_HE_PHY_INFO_TYPE_SU = 0x0, + IWL_RX_HE_PHY_INFO_TYPE_MU = 0x1, + IWL_RX_HE_PHY_INFO_TYPE_MU_EXT_INFO = 0x2, + IWL_RX_HE_PHY_INFO_TYPE_TB_EXT_INFO = 0x3, + + /* second dword - MU data */ + IWL_RX_HE_PHY_MU_SIGB_COMPRESSION = BIT_ULL(32 + 0), + IWL_RX_HE_PHY_MU_SIBG_SYM_OR_USER_NUM_MASK = 0x1e00000000ULL, + IWL_RX_HE_PHY_MU_SIGB_MCS_MASK = 0xf000000000000ULL, + IWL_RX_HE_PHY_MU_SIGB_DCM = BIT_ULL(32 + 21), + IWL_RX_HE_PHY_MU_PREAMBLE_PUNC_TYPE_MASK = 0xc0000000000000ULL, + + /* second dword - TB data */ + IWL_RX_HE_PHY_TB_PILOT_TYPE = BIT_ULL(32 + 0), + IWL_RX_HE_PHY_TB_LOW_SS_MASK = 0xe00000000ULL +}; + +enum iwl_rx_he_sigb_common0 { + /* the a1/a2/... is what the PHY/firmware calls the values */ + IWL_RX_HE_SIGB_COMMON0_CH1_RU0 = 0x000000ff, /* a1 */ + IWL_RX_HE_SIGB_COMMON0_CH1_RU2 = 0x0000ff00, /* a2 */ + IWL_RX_HE_SIGB_COMMON0_CH2_RU0 = 0x00ff0000, /* b1 */ + IWL_RX_HE_SIGB_COMMON0_CH2_RU2 = 0xff000000, /* b2 */ +}; + +enum iwl_rx_he_sigb_common1 { + IWL_RX_HE_SIGB_COMMON1_CH1_RU1 = 0x000000ff, /* c1 */ + IWL_RX_HE_SIGB_COMMON1_CH1_RU3 = 0x0000ff00, /* c2 */ + IWL_RX_HE_SIGB_COMMON1_CH2_RU1 = 0x00ff0000, /* d1 */ + IWL_RX_HE_SIGB_COMMON1_CH2_RU3 = 0xff000000, /* d2 */ +}; + +enum iwl_rx_he_sigb_common2 { + IWL_RX_HE_SIGB_COMMON2_CH1_CTR_RU = 0x0001, + IWL_RX_HE_SIGB_COMMON2_CH2_CTR_RU = 0x0002, + IWL_RX_HE_SIGB_COMMON2_CH1_CRC_OK = 0x0004, + IWL_RX_HE_SIGB_COMMON2_CH2_CRC_OK = 0x0008, }; /** @@ -381,15 +412,31 @@ enum iwl_rx_he_phy { */ struct iwl_rx_mpdu_desc_v1 { /* DW7 - carries rss_hash only when rpa_en == 1 */ - /** - * @rss_hash: RSS hash value - */ - __le32 rss_hash; + union { + /** + * @rss_hash: RSS hash value + */ + __le32 rss_hash; + + /** + * @sigb_common0: for HE sniffer, HE-SIG-B common part 0 + */ + __le32 sigb_common0; + }; + /* DW8 - carries filter_match only when rpa_en == 1 */ - /** - * @filter_match: filter match value - */ - __le32 filter_match; + union { + /** + * @filter_match: filter match value + */ + __le32 filter_match; + + /** + * @sigb_common1: for HE sniffer, HE-SIG-B common part 1 + */ + __le32 sigb_common1; + }; + /* DW9 */ /** * @rate_n_flags: RX rate/flags encoding @@ -439,15 +486,30 @@ struct iwl_rx_mpdu_desc_v1 { */ struct iwl_rx_mpdu_desc_v3 { /* DW7 - carries filter_match only when rpa_en == 1 */ - /** - * @filter_match: filter match value - */ - __le32 filter_match; + union { + /** + * @filter_match: filter match value + */ + __le32 filter_match; + + /** + * @sigb_common0: for HE sniffer, HE-SIG-B common part 0 + */ + __le32 sigb_common0; + }; + /* DW8 - carries rss_hash only when rpa_en == 1 */ - /** - * @rss_hash: RSS hash value - */ - __le32 rss_hash; + union { + /** + * @rss_hash: RSS hash value + */ + __le32 rss_hash; + + /** + * @sigb_common1: for HE sniffer, HE-SIG-B common part 1 + */ + __le32 sigb_common1; + }; /* DW9 */ /** * @partial_hash: 31:0 ip/tcp header hash @@ -543,10 +605,18 @@ struct iwl_rx_mpdu_desc { * @raw_csum: raw checksum (alledgedly unreliable) */ __le16 raw_csum; - /** - * @l3l4_flags: &enum iwl_rx_l3l4_flags - */ - __le16 l3l4_flags; + + union { + /** + * @l3l4_flags: &enum iwl_rx_l3l4_flags + */ + __le16 l3l4_flags; + + /** + * @sigb_common2: for HE sniffer, HE-SIG-B common part 2 + */ + __le16 sigb_common2; + }; /* DW5 */ /** * @status: &enum iwl_rx_mpdu_status @@ -574,6 +644,69 @@ struct iwl_rx_mpdu_desc { #define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1) +#define IWL_CD_STTS_OPTIMIZED_POS 0 +#define IWL_CD_STTS_OPTIMIZED_MSK 0x01 +#define IWL_CD_STTS_TRANSFER_STATUS_POS 1 +#define IWL_CD_STTS_TRANSFER_STATUS_MSK 0x0E +#define IWL_CD_STTS_WIFI_STATUS_POS 4 +#define IWL_CD_STTS_WIFI_STATUS_MSK 0xF0 + +/** + * enum iwl_completion_desc_transfer_status - transfer status (bits 1-3) + * @IWL_CD_STTS_UNUSED: unused + * @IWL_CD_STTS_UNUSED_2: unused + * @IWL_CD_STTS_END_TRANSFER: successful transfer complete. + * In sniffer mode, when split is used, set in last CD completion. (RX) + * @IWL_CD_STTS_OVERFLOW: In sniffer mode, when using split - used for + * all CD completion. (RX) + * @IWL_CD_STTS_ABORTED: CR abort / close flow. (RX) + * @IWL_CD_STTS_ERROR: general error (RX) + */ +enum iwl_completion_desc_transfer_status { + IWL_CD_STTS_UNUSED, + IWL_CD_STTS_UNUSED_2, + IWL_CD_STTS_END_TRANSFER, + IWL_CD_STTS_OVERFLOW, + IWL_CD_STTS_ABORTED, + IWL_CD_STTS_ERROR, +}; + +/** + * enum iwl_completion_desc_wifi_status - wifi status (bits 4-7) + * @IWL_CD_STTS_VALID: the packet is valid (RX) + * @IWL_CD_STTS_FCS_ERR: frame check sequence error (RX) + * @IWL_CD_STTS_SEC_KEY_ERR: error handling the security key of rx (RX) + * @IWL_CD_STTS_DECRYPTION_ERR: error decrypting the frame (RX) + * @IWL_CD_STTS_DUP: duplicate packet (RX) + * @IWL_CD_STTS_ICV_MIC_ERR: MIC error (RX) + * @IWL_CD_STTS_INTERNAL_SNAP_ERR: problems removing the snap (RX) + * @IWL_CD_STTS_SEC_PORT_FAIL: security port fail (RX) + * @IWL_CD_STTS_BA_OLD_SN: block ack received old SN (RX) + * @IWL_CD_STTS_QOS_NULL: QoS null packet (RX) + * @IWL_CD_STTS_MAC_HDR_ERR: MAC header conversion error (RX) + * @IWL_CD_STTS_MAX_RETRANS: reached max number of retransmissions (TX) + * @IWL_CD_STTS_EX_LIFETIME: exceeded lifetime (TX) + * @IWL_CD_STTS_NOT_USED: completed but not used (RX) + * @IWL_CD_STTS_REPLAY_ERR: pn check failed, replay error (RX) + */ +enum iwl_completion_desc_wifi_status { + IWL_CD_STTS_VALID, + IWL_CD_STTS_FCS_ERR, + IWL_CD_STTS_SEC_KEY_ERR, + IWL_CD_STTS_DECRYPTION_ERR, + IWL_CD_STTS_DUP, + IWL_CD_STTS_ICV_MIC_ERR, + IWL_CD_STTS_INTERNAL_SNAP_ERR, + IWL_CD_STTS_SEC_PORT_FAIL, + IWL_CD_STTS_BA_OLD_SN, + IWL_CD_STTS_QOS_NULL, + IWL_CD_STTS_MAC_HDR_ERR, + IWL_CD_STTS_MAX_RETRANS, + IWL_CD_STTS_EX_LIFETIME, + IWL_CD_STTS_NOT_USED, + IWL_CD_STTS_REPLAY_ERR, +}; + struct iwl_frame_release { u8 baid; u8 reserved; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index a17c4a79b8d4..310b01e3cce1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -262,6 +262,7 @@ enum iwl_scan_channel_flags { IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), + IWL_SCAN_CHANNEL_FLAG_EBS_FRAG = BIT(3), }; /* struct iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index dc40cbd52f92..450227f81706 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -30,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -391,7 +393,7 @@ enum iwl_sta_type { * @tfd_queue_msk: tfd queues used by this station. * Obselete for new TX API (9 and above). * @rx_ba_window: aggregation window size - * @sp_length: the size of the SP as it appears in the WME IE + * @sp_length: the size of the SP in actual number of frames * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver * enabled ACs. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 514b86123d3d..358bdf051e83 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -186,7 +186,7 @@ enum iwl_tx_cmd_sec_ctrl { /* * TID for non QoS frames - to be written in tid_tspec */ -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT +#define IWL_TID_NON_QOS 0 /* * Limits on the retransmissions - to be written in {data,rts}_retry_limit @@ -747,9 +747,9 @@ enum iwl_mvm_ba_resp_flags { * @tfd_cnt: number of TFD-Q elements * @ra_tid_cnt: number of RATID-Q elements * @tfd: array of TFD queue status updates. See &iwl_mvm_compressed_ba_tfd - * for details. + * for details. Length in @tfd_cnt. * @ra_tid: array of RA-TID queue status updates. For debug purposes only. See - * &iwl_mvm_compressed_ba_ratid for more details. + * &iwl_mvm_compressed_ba_ratid for more details. Length in @ra_tid_cnt. */ struct iwl_mvm_compressed_ba_notif { __le32 flags; @@ -766,7 +766,7 @@ struct iwl_mvm_compressed_ba_notif { __le32 tx_rate; __le16 tfd_cnt; __le16 ra_tid_cnt; - struct iwl_mvm_compressed_ba_tfd tfd[1]; + struct iwl_mvm_compressed_ba_tfd tfd[0]; struct iwl_mvm_compressed_ba_ratid ra_tid[0]; } __packed; /* COMPRESSED_BA_RES_API_S_VER_4 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index a31a42e673c4..0dcf1a673478 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -19,9 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -458,8 +455,8 @@ static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = { { .start = 0x00a02400, .end = 0x00a02758 }, }; -static void _iwl_read_prph_block(struct iwl_trans *trans, u32 start, - u32 len_bytes, __le32 *data) +static void iwl_read_prph_block(struct iwl_trans *trans, u32 start, + u32 len_bytes, __le32 *data) { u32 i; @@ -467,21 +464,6 @@ static void _iwl_read_prph_block(struct iwl_trans *trans, u32 start, *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i)); } -static bool iwl_read_prph_block(struct iwl_trans *trans, u32 start, - u32 len_bytes, __le32 *data) -{ - unsigned long flags; - bool success = false; - - if (iwl_trans_grab_nic_access(trans, &flags)) { - success = true; - _iwl_read_prph_block(trans, start, len_bytes, data); - iwl_trans_release_nic_access(trans, &flags); - } - - return success; -} - static void iwl_dump_prph(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data, const struct iwl_prph_range *iwl_prph_dump_addr, @@ -507,11 +489,11 @@ static void iwl_dump_prph(struct iwl_trans *trans, prph = (void *)(*data)->data; prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); - _iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, - /* our range is inclusive, hence + 4 */ - iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4, - (void *)prph->data); + iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, + /* our range is inclusive, hence + 4 */ + iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4, + (void *)prph->data); *data = iwl_fw_error_next_data(*data); } @@ -556,12 +538,108 @@ static struct scatterlist *alloc_sgtable(int size) return table; } +static int iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt) +{ + u32 prph_len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr_comm); + i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = + iwl_prph_dump_addr_comm[i].end - + iwl_prph_dump_addr_comm[i].start + 4; + + prph_len += sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + + if (fwrt->trans->cfg->mq_rx_supported) { + for (i = 0; i < + ARRAY_SIZE(iwl_prph_dump_addr_9000); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = + iwl_prph_dump_addr_9000[i].end - + iwl_prph_dump_addr_9000[i].start + 4; + + prph_len += sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + } + return prph_len; +} + +static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data, + u32 sram_len, u32 sram_ofs, u32 smem_len, + u32 sram2_len) +{ + const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = fwrt->fw->dbg_mem_tlv; + struct iwl_fw_error_dump_mem *dump_mem; + int i; + + if (!fwrt->fw->n_dbg_mem_tlv) { + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + (*dump_data)->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); + dump_mem = (void *)(*dump_data)->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(sram_ofs); + iwl_trans_read_mem_bytes(fwrt->trans, sram_ofs, dump_mem->data, + sram_len); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + for (i = 0; i < fwrt->fw->n_dbg_mem_tlv; i++) { + u32 len = le32_to_cpu(fw_dbg_mem[i].len); + u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); + + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + (*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem)); + dump_mem = (void *)(*dump_data)->data; + dump_mem->type = fw_dbg_mem[i].data_type; + dump_mem->offset = cpu_to_le32(ofs); + + IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", + dump_mem->type); + + iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + if (smem_len) { + IWL_DEBUG_INFO(fwrt, "WRT SMEM dump\n"); + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + (*dump_data)->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); + dump_mem = (void *)(*dump_data)->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); + dump_mem->offset = cpu_to_le32(fwrt->trans->cfg->smem_offset); + iwl_trans_read_mem_bytes(fwrt->trans, + fwrt->trans->cfg->smem_offset, + dump_mem->data, smem_len); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + if (sram2_len) { + IWL_DEBUG_INFO(fwrt, "WRT SRAM dump\n"); + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + (*dump_data)->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); + dump_mem = (void *)(*dump_data)->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(fwrt->trans->cfg->dccm2_offset); + iwl_trans_read_mem_bytes(fwrt->trans, + fwrt->trans->cfg->dccm2_offset, + dump_mem->data, sram2_len); + *dump_data = iwl_fw_error_next_data(*dump_data); + } +} + void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) { struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; - struct iwl_fw_error_dump_mem *dump_mem; struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg; struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_fw_dump_ptrs *fw_error_dump; @@ -673,35 +751,8 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) /* Make room for PRPH registers */ if (!fwrt->trans->cfg->gen2 && - fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PRPH)) { - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr_comm); - i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = - iwl_prph_dump_addr_comm[i].end - - iwl_prph_dump_addr_comm[i].start + 4; - - prph_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - } - - if (!fwrt->trans->cfg->gen2 && - fwrt->trans->cfg->mq_rx_supported && - fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PRPH)) { - for (i = 0; i < - ARRAY_SIZE(iwl_prph_dump_addr_9000); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = - iwl_prph_dump_addr_9000[i].end - - iwl_prph_dump_addr_9000[i].start + 4; - - prph_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - } + fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PRPH)) + prph_len += iwl_fw_get_prph_len(fwrt); if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 && fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RADIO_REG)) @@ -721,18 +772,19 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) if (fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) { /* Make room for the SMEM, if it exists */ if (smem_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - smem_len; + file_len += sizeof(*dump_data) + smem_len + + sizeof(struct iwl_fw_error_dump_mem); /* Make room for the secondary SRAM, if it exists */ if (sram2_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - sram2_len; + file_len += sizeof(*dump_data) + sram2_len + + sizeof(struct iwl_fw_error_dump_mem); /* Make room for MEM segments */ for (i = 0; i < fwrt->fw->n_dbg_mem_tlv; i++) { - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - le32_to_cpu(fw_dbg_mem[i].len); + file_len += sizeof(*dump_data) + + le32_to_cpu(fw_dbg_mem[i].len) + + sizeof(struct iwl_fw_error_dump_mem); } } @@ -746,6 +798,11 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) sizeof(struct iwl_fw_error_dump_paging) + PAGING_BLOCK_SIZE); + if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { + file_len += sizeof(*dump_data) + + fwrt->trans->cfg->d3_debug_data_length * 2; + } + /* If we only want a monitor dump, reset the file length */ if (monitor_dump_only) { file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + @@ -759,7 +816,8 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) if (fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM) && !fwrt->fw->n_dbg_mem_tlv) - file_len += sizeof(*dump_data) + sram_len + sizeof(*dump_mem); + file_len += sizeof(*dump_data) + sram_len + + sizeof(struct iwl_fw_error_dump_mem); dump_file = vzalloc(file_len); if (!dump_file) { @@ -824,7 +882,7 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) } /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { + if (fifo_data_len) { iwl_fw_dump_fifos(fwrt, &dump_data); if (radio_len) iwl_read_radio_regs(fwrt, &dump_data); @@ -846,82 +904,27 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) if (monitor_dump_only) goto dump_trans_data; - if (!fwrt->fw->n_dbg_mem_tlv && - fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(sram_ofs); - iwl_trans_read_mem_bytes(fwrt->trans, sram_ofs, dump_mem->data, - sram_len); - dump_data = iwl_fw_error_next_data(dump_data); - } + if (fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) + iwl_fw_dump_mem(fwrt, &dump_data, sram_len, sram_ofs, smem_len, + sram2_len); - for (i = 0; i < fwrt->fw->n_dbg_mem_tlv; i++) { - u32 len = le32_to_cpu(fw_dbg_mem[i].len); - u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); - bool success; - if (!(fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM))) - break; + if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { + u32 addr = fwrt->trans->cfg->d3_debug_data_base_addr; + size_t data_size = fwrt->trans->cfg->d3_debug_data_length; - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = fw_dbg_mem[i].data_type; - dump_mem->offset = cpu_to_le32(ofs); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); + dump_data->len = cpu_to_le32(data_size * 2); - IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", - dump_mem->type); + memcpy(dump_data->data, fwrt->dump.d3_debug_data, data_size); - switch (dump_mem->type & cpu_to_le32(FW_DBG_MEM_TYPE_MASK)) { - case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR): - iwl_trans_read_mem_bytes(fwrt->trans, ofs, - dump_mem->data, - len); - success = true; - break; - case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH): - success = iwl_read_prph_block(fwrt->trans, ofs, len, - (void *)dump_mem->data); - break; - default: - /* - * shouldn't get here, we ignored this kind - * of TLV earlier during the TLV parsing?! - */ - WARN_ON(1); - success = false; - } + kfree(fwrt->dump.d3_debug_data); + fwrt->dump.d3_debug_data = NULL; - if (success) - dump_data = iwl_fw_error_next_data(dump_data); - } + iwl_trans_read_mem_bytes(fwrt->trans, addr, + dump_data->data + data_size, + data_size); - if (smem_len && fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) { - IWL_DEBUG_INFO(fwrt, "WRT SMEM dump\n"); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); - dump_mem->offset = cpu_to_le32(fwrt->trans->cfg->smem_offset); - iwl_trans_read_mem_bytes(fwrt->trans, - fwrt->trans->cfg->smem_offset, - dump_mem->data, smem_len); - dump_data = iwl_fw_error_next_data(dump_data); - } - - if (sram2_len && fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) { - IWL_DEBUG_INFO(fwrt, "WRT SRAM dump\n"); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(fwrt->trans->cfg->dccm2_offset); - iwl_trans_read_mem_bytes(fwrt->trans, - fwrt->trans->cfg->dccm2_offset, - dump_mem->data, sram2_len); dump_data = iwl_fw_error_next_data(dump_data); } @@ -1016,7 +1019,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, * 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 + * in such a case - 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. * @@ -1024,9 +1027,8 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, * 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")) + if (fwrt->trans->state == IWL_TRANS_NO_FW && + fwrt->smem_cfg.num_lmacs) return -EIO; if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) @@ -1133,9 +1135,6 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) IWL_WARN(fwrt, "FW already configured (%d) - re-configuring\n", fwrt->dump.conf); - /* start default config marker cmd for syncing logs */ - iwl_fw_trigger_timestamp(fwrt, 1); - /* Send all HCMDs for configuring the FW debug */ ptr = (void *)&fwrt->fw->dbg_conf_tlv[conf_id]->hcmd; for (i = 0; i < fwrt->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { @@ -1164,6 +1163,7 @@ void iwl_fw_error_dump_wk(struct work_struct *work) { struct iwl_fw_runtime *fwrt = container_of(work, struct iwl_fw_runtime, dump.wk.work); + struct iwl_fw_dbg_params params = {0}; if (fwrt->ops && fwrt->ops->dump_start && fwrt->ops->dump_start(fwrt->ops_ctx)) @@ -1177,41 +1177,42 @@ void iwl_fw_error_dump_wk(struct work_struct *work) goto out; } - if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - /* stop recording */ - iwl_fw_dbg_stop_recording(fwrt); - - iwl_fw_error_dump(fwrt); - - /* start recording again if the firmware is not crashed */ - if (!test_bit(STATUS_FW_ERROR, &fwrt->trans->status) && - fwrt->fw->dbg_dest_tlv) { - iwl_clear_bits_prph(fwrt->trans, - MON_BUFF_SAMPLE_CTL, 0x100); - iwl_clear_bits_prph(fwrt->trans, - MON_BUFF_SAMPLE_CTL, 0x1); - iwl_set_bits_prph(fwrt->trans, - MON_BUFF_SAMPLE_CTL, 0x1); - } - } else { - u32 in_sample = iwl_read_prph(fwrt->trans, DBGC_IN_SAMPLE); - u32 out_ctrl = iwl_read_prph(fwrt->trans, DBGC_OUT_CTRL); + iwl_fw_dbg_stop_recording(fwrt, ¶ms); - iwl_fw_dbg_stop_recording(fwrt); + iwl_fw_error_dump(fwrt); + + /* start recording again if the firmware is not crashed */ + if (!test_bit(STATUS_FW_ERROR, &fwrt->trans->status) && + fwrt->fw->dbg_dest_tlv) { /* wait before we collect the data till the DBGC stop */ udelay(500); - - iwl_fw_error_dump(fwrt); - - /* start recording again if the firmware is not crashed */ - if (!test_bit(STATUS_FW_ERROR, &fwrt->trans->status) && - fwrt->fw->dbg_dest_tlv) { - iwl_write_prph(fwrt->trans, DBGC_IN_SAMPLE, in_sample); - iwl_write_prph(fwrt->trans, DBGC_OUT_CTRL, out_ctrl); - } + iwl_fw_dbg_restart_recording(fwrt, ¶ms); } out: if (fwrt->ops && fwrt->ops->dump_end) fwrt->ops->dump_end(fwrt->ops_ctx); } +void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) +{ + const struct iwl_cfg *cfg = fwrt->trans->cfg; + + if (!iwl_fw_dbg_is_d3_debug_enabled(fwrt)) + return; + + if (!fwrt->dump.d3_debug_data) { + fwrt->dump.d3_debug_data = kmalloc(cfg->d3_debug_data_length, + GFP_KERNEL); + if (!fwrt->dump.d3_debug_data) { + IWL_ERR(fwrt, + "failed to allocate memory for D3 debug data\n"); + return; + } + } + + /* if the buffer holds previous debug data it is overwritten */ + iwl_trans_read_mem_bytes(fwrt->trans, cfg->d3_debug_data_base_addr, + fwrt->dump.d3_debug_data, + cfg->d3_debug_data_length); +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 507d9a49fa97..3c89230fae6a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -19,9 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -74,6 +71,7 @@ #include "iwl-io.h" #include "file.h" #include "error-dump.h" +#include "api/commands.h" /** * struct iwl_fw_dump_desc - describes the dump @@ -86,6 +84,16 @@ struct iwl_fw_dump_desc { struct iwl_fw_error_dump_trigger_desc trig_desc; }; +/** + * struct iwl_fw_dbg_params - register values to restore + * @in_sample: DBGC_IN_SAMPLE value + * @out_ctrl: DBGC_OUT_CTRL value + */ +struct iwl_fw_dbg_params { + u32 in_sample; + u32 out_ctrl; +}; + extern const struct iwl_fw_dump_desc iwl_dump_desc_assert; static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt) @@ -199,17 +207,80 @@ _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt, iwl_fw_dbg_get_trigger((fwrt)->fw,\ (trig))) -static inline void iwl_fw_dbg_stop_recording(struct iwl_fw_runtime *fwrt) +static int iwl_fw_dbg_start_stop_hcmd(struct iwl_fw_runtime *fwrt, bool start) +{ + struct iwl_continuous_record_cmd cont_rec = {}; + struct iwl_host_cmd hcmd = { + .id = LDBG_CONFIG_CMD, + .flags = CMD_ASYNC, + .data[0] = &cont_rec, + .len[0] = sizeof(cont_rec), + }; + + cont_rec.record_mode.enable_recording = start ? + cpu_to_le16(START_DEBUG_RECORDING) : + cpu_to_le16(STOP_DEBUG_RECORDING); + + return iwl_trans_send_cmd(fwrt->trans, &hcmd); +} + +static inline void +_iwl_fw_dbg_stop_recording(struct iwl_trans *trans, + struct iwl_fw_dbg_params *params) +{ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); + return; + } + + if (params) { + params->in_sample = iwl_read_prph(trans, DBGC_IN_SAMPLE); + params->out_ctrl = iwl_read_prph(trans, DBGC_OUT_CTRL); + } + + iwl_write_prph(trans, DBGC_IN_SAMPLE, 0); + udelay(100); + iwl_write_prph(trans, DBGC_OUT_CTRL, 0); +} + +static inline void +iwl_fw_dbg_stop_recording(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dbg_params *params) +{ + if (fwrt->trans->cfg->device_family < IWL_DEVICE_FAMILY_22560) + _iwl_fw_dbg_stop_recording(fwrt->trans, params); + else + iwl_fw_dbg_start_stop_hcmd(fwrt, false); +} + +static inline void +_iwl_fw_dbg_restart_recording(struct iwl_trans *trans, + struct iwl_fw_dbg_params *params) { - if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - iwl_set_bits_prph(fwrt->trans, MON_BUFF_SAMPLE_CTL, 0x100); + if (WARN_ON(!params)) + return; + + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); + iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); + iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); } else { - iwl_write_prph(fwrt->trans, DBGC_IN_SAMPLE, 0); + iwl_write_prph(trans, DBGC_IN_SAMPLE, params->in_sample); udelay(100); - iwl_write_prph(fwrt->trans, DBGC_OUT_CTRL, 0); + iwl_write_prph(trans, DBGC_OUT_CTRL, params->out_ctrl); } } +static inline void +iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dbg_params *params) +{ + if (fwrt->trans->cfg->device_family < IWL_DEVICE_FAMILY_22560) + _iwl_fw_dbg_restart_recording(fwrt->trans, params); + else + iwl_fw_dbg_start_stop_hcmd(fwrt, true); +} + static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt) { fwrt->dump.conf = FW_DBG_INVALID; @@ -217,6 +288,16 @@ static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt) void iwl_fw_error_dump_wk(struct work_struct *work); +static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt) +{ + return fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_D3_DEBUG) && + fwrt->trans->cfg->d3_debug_data_length && + fwrt->fw->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); +} + +void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt); + static inline void iwl_fw_flush_dump(struct iwl_fw_runtime *fwrt) { flush_delayed_work(&fwrt->dump.wk); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index 8ba5a60ec9ed..1049bdfe1e69 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * 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 @@ -18,9 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -33,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,55 +65,117 @@ #include "debugfs.h" #include "dbg.h" -#define FWRT_DEBUGFS_READ_FILE_OPS(name) \ -static ssize_t iwl_dbgfs_##name##_read(struct iwl_fw_runtime *fwrt, \ - char *buf, size_t count, \ - loff_t *ppos); \ +#define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +struct dbgfs_##name##_data { \ + argtype *arg; \ + bool read_done; \ + ssize_t rlen; \ + char rbuf[buflen]; \ +}; \ +static int _iwl_dbgfs_##name##_open(struct inode *inode, \ + struct file *file) \ +{ \ + struct dbgfs_##name##_data *data; \ + \ + data = kzalloc(sizeof(*data), GFP_KERNEL); \ + if (!data) \ + return -ENOMEM; \ + \ + data->read_done = false; \ + data->arg = inode->i_private; \ + file->private_data = data; \ + \ + return 0; \ +} + +#define FWRT_DEBUGFS_READ_WRAPPER(name) \ +static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + \ + if (!data->read_done) { \ + data->read_done = true; \ + data->rlen = iwl_dbgfs_##name##_read(data->arg, \ + sizeof(data->rbuf),\ + data->rbuf); \ + } \ + \ + if (data->rlen < 0) \ + return data->rlen; \ + return simple_read_from_buffer(user_buf, count, ppos, \ + data->rbuf, data->rlen); \ +} + +static int _iwl_dbgfs_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +#define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \ +FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +FWRT_DEBUGFS_READ_WRAPPER(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ } -#define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen) \ -static ssize_t iwl_dbgfs_##name##_write(struct iwl_fw_runtime *fwrt, \ - char *buf, size_t count, \ - loff_t *ppos); \ +#define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos) \ { \ - struct iwl_fw_runtime *fwrt = file->private_data; \ + argtype *arg = \ + ((struct dbgfs_##name##_data *)file->private_data)->arg;\ char buf[buflen] = {}; \ size_t buf_size = min(count, sizeof(buf) - 1); \ \ if (copy_from_user(buf, user_buf, buf_size)) \ return -EFAULT; \ \ - return iwl_dbgfs_##name##_write(fwrt, buf, buf_size, ppos); \ + return iwl_dbgfs_##name##_write(arg, buf, buf_size); \ } -#define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen) \ -FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen) \ +#define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ +FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +FWRT_DEBUGFS_READ_WRAPPER(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = _iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ } -#define FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen) \ -FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen) \ +#define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ +FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = _iwl_dbgfs_##name##_write, \ - .open = simple_open, \ + .open = _iwl_dbgfs_##name##_open, \ .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ } +#define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz) \ + _FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime) + +#define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime) + +#define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime) + #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ - if (!debugfs_create_file(alias, mode, parent, fwrt, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ + if (!debugfs_create_file(alias, mode, parent, fwrt, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ } while (0) #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \ FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) @@ -173,8 +234,7 @@ void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay) } static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt, - char *buf, size_t count, - loff_t *ppos) + char *buf, size_t count) { int ret; u32 delay; @@ -188,7 +248,15 @@ static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt, return count; } -FWRT_DEBUGFS_WRITE_FILE_OPS(timestamp_marker, 10); +static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt, + size_t size, char *buf) +{ + u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000; + + return scnprintf(buf, size, "%d\n", delay_secs); +} + +FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16); int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, struct dentry *dbgfs_dir) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h index cbbfa8e9e66d..88255035e8ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h @@ -18,9 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index ed7beca8817e..6d3ef331b7d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -8,6 +8,7 @@ * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * 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 @@ -18,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -35,6 +31,7 @@ * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -116,6 +113,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_INTERNAL_TXF = 14, IWL_FW_ERROR_DUMP_EXTERNAL = 15, /* Do not move */ IWL_FW_ERROR_DUMP_MEM_CFG = 16, + IWL_FW_ERROR_DUMP_D3_DEBUG_DATA = 17, IWL_FW_ERROR_DUMP_MAX, }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index bbf2b265a06a..63e277b07b8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -258,6 +253,9 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * deprecated. * @IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2: This ucode supports version 8 * of scan request: SCAN_REQUEST_CMD_UMAC_API_S_VER_8 + * @IWL_UCODE_TLV_API_FRAG_EBS: This ucode supports fragmented EBS + * @IWL_UCODE_TLV_API_REDUCE_TX_POWER: This ucode supports v5 of + * the REDUCE_TX_POWER_CMD. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -276,9 +274,12 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_OCE = (__force iwl_ucode_tlv_api_t)33, 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_WOWLAN_KEY_MATERIAL = (__force iwl_ucode_tlv_api_t)36, IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY = (__force iwl_ucode_tlv_api_t)38, IWL_UCODE_TLV_API_DEPRECATE_TTAK = (__force iwl_ucode_tlv_api_t)41, IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2 = (__force iwl_ucode_tlv_api_t)42, + IWL_UCODE_TLV_API_FRAG_EBS = (__force iwl_ucode_tlv_api_t)44, + IWL_UCODE_TLV_API_REDUCE_TX_POWER = (__force iwl_ucode_tlv_api_t)45, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ @@ -325,6 +326,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @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_DYNAMIC_QUOTA: firmware implements quota related + * @IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2: firmware implements Coex Schema 2 * @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 @@ -349,6 +351,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * command size (command version 4) that supports toggling ACK TX * power reduction. * @IWL_UCODE_TLV_CAPA_MLME_OFFLOAD: supports MLME offload + * @IWL_UCODE_TLV_CAPA_D3_DEBUG: supports debug recording during D3 * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -381,6 +384,7 @@ enum iwl_ucode_tlv_capa { 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_DYNAMIC_QUOTA = (__force iwl_ucode_tlv_capa_t)44, + IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2 = (__force iwl_ucode_tlv_capa_t)45, 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, @@ -396,7 +400,8 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG = (__force iwl_ucode_tlv_capa_t)80, IWL_UCODE_TLV_CAPA_LQM_SUPPORT = (__force iwl_ucode_tlv_capa_t)81, IWL_UCODE_TLV_CAPA_TX_POWER_ACK = (__force iwl_ucode_tlv_capa_t)84, - IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT = (__force iwl_ucode_tlv_capa_t)86, + IWL_UCODE_TLV_CAPA_D3_DEBUG = (__force iwl_ucode_tlv_capa_t)87, + IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT = (__force iwl_ucode_tlv_capa_t)88, IWL_UCODE_TLV_CAPA_MLME_OFFLOAD = (__force iwl_ucode_tlv_capa_t)96, NUM_IWL_UCODE_TLV_CAPA @@ -528,22 +533,9 @@ enum iwl_fw_dbg_monitor_mode { }; /** - * enum iwl_fw_mem_seg_type - memory segment type - * @FW_DBG_MEM_TYPE_MASK: mask for the type indication - * @FW_DBG_MEM_TYPE_REGULAR: regular memory - * @FW_DBG_MEM_TYPE_PRPH: periphery memory (requires special reading) - */ -enum iwl_fw_mem_seg_type { - FW_DBG_MEM_TYPE_MASK = 0xff000000, - FW_DBG_MEM_TYPE_REGULAR = 0x00000000, - FW_DBG_MEM_TYPE_PRPH = 0x01000000, -}; - -/** * struct iwl_fw_dbg_mem_seg_tlv - configures the debug data memory segments * - * @data_type: the memory segment type to record, see &enum iwl_fw_mem_seg_type - * for what we care about + * @data_type: the memory segment type to record * @ofs: the memory segment offset * @len: the memory segment length, in bytes * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index 0861b97c4233..9cc8fe8908ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c b/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c index 1096c945a68b..379735e086dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.h b/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.h index 368884be4e7c..61b067eeeac9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/notif-wait.h @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index ed23367f7088..9ed5819defaf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -136,6 +136,7 @@ struct iwl_fw_runtime { /* ts of the beginning of a non-collect fw dbg data period */ unsigned long non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1]; + u32 *d3_debug_data; } dump; #ifdef CONFIG_IWLWIFI_DEBUGFS struct { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h index ee9347a54cdc..359537620c93 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 12fddcf15bab..5eb906a0d0d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -388,6 +383,8 @@ struct iwl_csr_params { * @gen2: 22000 and on transport operation * @cdb: CDB support * @nvm_type: see &enum iwl_nvm_type + * @d3_debug_data_base_addr: base address where D3 debug data is stored + * @d3_debug_data_length: length of the D3 debug data * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -452,6 +449,8 @@ struct iwl_cfg { u8 ucode_api_min; u32 min_umac_error_event_table; u32 extra_phy_cfg_flags; + u32 d3_debug_data_base_addr; + u32 d3_debug_data_length; }; static const struct iwl_csr_params iwl_csr_v1 = { @@ -574,11 +573,18 @@ 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 iwl9461_2ac_cfg_qu_b0_jf_b0; +extern const struct iwl_cfg iwl9462_2ac_cfg_qu_b0_jf_b0; +extern const struct iwl_cfg iwl9560_2ac_cfg_qu_b0_jf_b0; +extern const struct iwl_cfg killer1550i_2ac_cfg_qu_b0_jf_b0; +extern const struct iwl_cfg killer1550s_2ac_cfg_qu_b0_jf_b0; +extern const struct iwl_cfg iwl22000_2ax_cfg_jf; extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_a0_f0; +extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_b0_f0; extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_b0; extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_jf_b0; extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_a0; extern const struct iwl_cfg iwl22560_2ax_cfg_su_cdb; -#endif /* CONFIG_IWLMVM */ +#endif /* CPTCFG_IWLMVM || CPTCFG_IWLFMAC */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h index 4b6fdf3b15fb..5ed07e37e3ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h @@ -64,20 +64,41 @@ * the init done for driver command that configures several system modes * @IWL_CTXT_INFO_EARLY_DEBUG: enable early debug * @IWL_CTXT_INFO_ENABLE_CDMP: enable core dump - * @IWL_CTXT_INFO_RB_SIZE_4K: Use 4K RB size (the default is 2K) * @IWL_CTXT_INFO_RB_CB_SIZE_POS: position of the RBD Cyclic Buffer Size * exponent, the actual size is 2**value, valid sizes are 8-2048. * The value is four bits long. Maximum valid exponent is 12 * @IWL_CTXT_INFO_TFD_FORMAT_LONG: use long TFD Format (the * default is short format - not supported by the driver) + * @IWL_CTXT_INFO_RB_SIZE_POS: RB size position + * (values are IWL_CTXT_INFO_RB_SIZE_*K) + * @IWL_CTXT_INFO_RB_SIZE_1K: Value for 1K RB size + * @IWL_CTXT_INFO_RB_SIZE_2K: Value for 2K RB size + * @IWL_CTXT_INFO_RB_SIZE_4K: Value for 4K RB size + * @IWL_CTXT_INFO_RB_SIZE_8K: Value for 8K RB size + * @IWL_CTXT_INFO_RB_SIZE_12K: Value for 12K RB size + * @IWL_CTXT_INFO_RB_SIZE_16K: Value for 16K RB size + * @IWL_CTXT_INFO_RB_SIZE_20K: Value for 20K RB size + * @IWL_CTXT_INFO_RB_SIZE_24K: Value for 24K RB size + * @IWL_CTXT_INFO_RB_SIZE_28K: Value for 28K RB size + * @IWL_CTXT_INFO_RB_SIZE_32K: Value for 32K RB size */ enum iwl_context_info_flags { IWL_CTXT_INFO_AUTO_FUNC_INIT = BIT(0), IWL_CTXT_INFO_EARLY_DEBUG = BIT(1), IWL_CTXT_INFO_ENABLE_CDMP = BIT(2), - IWL_CTXT_INFO_RB_SIZE_4K = BIT(3), IWL_CTXT_INFO_RB_CB_SIZE_POS = 4, IWL_CTXT_INFO_TFD_FORMAT_LONG = BIT(8), + IWL_CTXT_INFO_RB_SIZE_POS = 9, + IWL_CTXT_INFO_RB_SIZE_1K = 0x1, + IWL_CTXT_INFO_RB_SIZE_2K = 0x2, + IWL_CTXT_INFO_RB_SIZE_4K = 0x4, + IWL_CTXT_INFO_RB_SIZE_8K = 0x8, + IWL_CTXT_INFO_RB_SIZE_12K = 0x9, + IWL_CTXT_INFO_RB_SIZE_16K = 0xa, + IWL_CTXT_INFO_RB_SIZE_20K = 0xb, + IWL_CTXT_INFO_RB_SIZE_24K = 0xc, + IWL_CTXT_INFO_RB_SIZE_28K = 0xd, + IWL_CTXT_INFO_RB_SIZE_32K = 0xe, }; /* diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 9019de99f077..caa5806acd81 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -188,6 +183,7 @@ #define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0) #define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) #define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) +#define CSR_HW_IF_CONFIG_REG_D3_DEBUG (0x00000200) #define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00) #define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000) #define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c index b1c3b0d0fcc6..e1a41fd503a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h index c023fcf5d452..a2af68a0d34b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -13,10 +13,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h index a80e4202cd03..2cc6c019d0e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h @@ -2,6 +2,7 @@ * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -12,10 +13,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -76,12 +73,11 @@ TRACE_EVENT(iwlwifi_dev_rx_data, TP_ARGS(dev, trans, rxbuf, len), TP_STRUCT__entry( DEV_ENTRY - __dynamic_array(u8, data, - len - iwl_rx_trace_len(trans, rxbuf, len)) + len - iwl_rx_trace_len(trans, rxbuf, len, NULL)) ), TP_fast_assign( - size_t offs = iwl_rx_trace_len(trans, rxbuf, len); + size_t offs = iwl_rx_trace_len(trans, rxbuf, len, NULL); DEV_ASSIGN; if (offs < len) memcpy(__get_dynamic_array(data), diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h index 4164dc1745ed..7bb4e0e9bb69 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h @@ -12,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h index 27e3e4e96aa2..8e87186682e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h @@ -3,6 +3,7 @@ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -13,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -75,13 +72,18 @@ TRACE_EVENT(iwlwifi_dev_rx, TP_STRUCT__entry( DEV_ENTRY __field(u16, cmd) - __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len)) + __field(u8, hdr_offset) + __dynamic_array(u8, rxbuf, + iwl_rx_trace_len(trans, pkt, len, NULL)) ), TP_fast_assign( + size_t hdr_offset = 0; + DEV_ASSIGN; __entry->cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); memcpy(__get_dynamic_array(rxbuf), pkt, - iwl_rx_trace_len(trans, pkt, len)); + iwl_rx_trace_len(trans, pkt, len, &hdr_offset)); + __entry->hdr_offset = hdr_offset; ), TP_printk("[%s] RX cmd %#.2x", __get_str(dev), __entry->cmd) @@ -126,61 +128,6 @@ TRACE_EVENT(iwlwifi_dev_tx, __entry->framelen, __entry->skbaddr) ); -struct iwl_error_event_table; -TRACE_EVENT(iwlwifi_dev_ucode_error, - TP_PROTO(const struct device *dev, const struct iwl_error_event_table *table, - u32 hw_ver, u32 brd_ver), - TP_ARGS(dev, table, hw_ver, brd_ver), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, desc) - __field(u32, tsf_low) - __field(u32, data1) - __field(u32, data2) - __field(u32, line) - __field(u32, blink2) - __field(u32, ilink1) - __field(u32, ilink2) - __field(u32, bcon_time) - __field(u32, gp1) - __field(u32, gp2) - __field(u32, rev_type) - __field(u32, major) - __field(u32, minor) - __field(u32, hw_ver) - __field(u32, brd_ver) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->desc = table->error_id; - __entry->tsf_low = table->tsf_low; - __entry->data1 = table->data1; - __entry->data2 = table->data2; - __entry->line = table->line; - __entry->blink2 = table->blink2; - __entry->ilink1 = table->ilink1; - __entry->ilink2 = table->ilink2; - __entry->bcon_time = table->bcon_time; - __entry->gp1 = table->gp1; - __entry->gp2 = table->gp2; - __entry->rev_type = table->gp3; - __entry->major = table->ucode_ver; - __entry->minor = table->hw_ver; - __entry->hw_ver = hw_ver; - __entry->brd_ver = brd_ver; - ), - TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " - "blink2 0x%05X ilink 0x%05X 0x%05X " - "bcon_tm %010u gp 0x%08X 0x%08X rev_type 0x%08X major 0x%08X " - "minor 0x%08X hw 0x%08X brd 0x%08X", - __get_str(dev), __entry->desc, __entry->tsf_low, - __entry->data1, __entry->data2, __entry->line, - __entry->blink2, __entry->ilink1, __entry->ilink2, - __entry->bcon_time, __entry->gp1, __entry->gp2, - __entry->rev_type, __entry->major, __entry->minor, - __entry->hw_ver, __entry->brd_ver) -); - TRACE_EVENT(iwlwifi_dev_ucode_event, TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), TP_ARGS(dev, time, data, ev), diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h index 5dfc9295a7e0..32984c1f39a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h index e9b8673dd245..53842226ef1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c index 6aa719865a58..9805432f124f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * Copyright (C) 2018 Intel Corporation * * 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 @@ -11,10 +12,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -30,12 +27,10 @@ #ifndef __CHECKER__ #include "iwl-trans.h" -#include "dvm/commands.h" #define CREATE_TRACE_POINTS #include "iwl-devtrace.h" EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); #endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h index f5c1127253cb..fc649b2bc017 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -1,7 +1,8 @@ /****************************************************************************** * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * Copyright(C) 2016 Intel Deutschland GmbH + * Copyright(C) 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -12,10 +13,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -60,16 +57,23 @@ static inline bool iwl_trace_data(struct sk_buff *skb) } static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, - void *rxbuf, size_t len) + void *rxbuf, size_t len, + size_t *out_hdr_offset) { struct iwl_cmd_header *cmd = (void *)((u8 *)rxbuf + sizeof(__le32)); - struct ieee80211_hdr *hdr; + struct ieee80211_hdr *hdr = NULL; + size_t hdr_offset; if (cmd->cmd != trans->rx_mpdu_cmd) return len; - hdr = (void *)((u8 *)cmd + sizeof(struct iwl_cmd_header) + - trans->rx_mpdu_cmd_hdr_size); + hdr_offset = sizeof(struct iwl_cmd_header) + + trans->rx_mpdu_cmd_hdr_size; + + if (out_hdr_offset) + *out_hdr_offset = hdr_offset; + + hdr = (void *)((u8 *)cmd + hdr_offset); if (!ieee80211_is_data(hdr->frame_control)) return len; /* maybe try to identify EAPOL frames? */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index c0631255aee7..d3a60d1aacb5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -1065,30 +1060,15 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_FW_MEM_SEG: { struct iwl_fw_dbg_mem_seg_tlv *dbg_mem = (void *)tlv_data; - u32 type; size_t size; struct iwl_fw_dbg_mem_seg_tlv *n; if (tlv_len != (sizeof(*dbg_mem))) goto invalid_tlv_len; - type = le32_to_cpu(dbg_mem->data_type); - IWL_DEBUG_INFO(drv, "Found debug memory segment: %u\n", dbg_mem->data_type); - switch (type & FW_DBG_MEM_TYPE_MASK) { - case FW_DBG_MEM_TYPE_REGULAR: - case FW_DBG_MEM_TYPE_PRPH: - /* we know how to handle these */ - break; - default: - IWL_ERR(drv, - "Found debug memory segment with invalid type: 0x%x\n", - type); - return -EINVAL; - } - size = sizeof(*pieces->dbg_mem_tlv) * (pieces->n_dbg_mem_tlv + 1); n = krealloc(pieces->dbg_mem_tlv, size, GFP_KERNEL); @@ -1275,8 +1255,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; - /* dump all fw memory areas by default */ - fw->dbg_dump_mask = 0xffffffff; + /* dump all fw memory areas by default except d3 debug data */ + fw->dbg_dump_mask = 0xfffdffff; pieces = kzalloc(sizeof(*pieces), GFP_KERNEL); if (!pieces) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index 1f8a2eeb7dff..2be30af7bdc3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index a4c96215933b..4e3422a1c7bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -18,9 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -745,7 +742,9 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, else rx_chains = hweight8(rx_chains); - if (!(data->sku_cap_11n_enable) || !cfg->ht_params) { + if (!(data->sku_cap_11n_enable) || + (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) || + !cfg->ht_params) { ht_info->ht_supported = false; return; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index 8be50ed12300..d910bda087f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c index ac965c34a2f8..a6db6a814257 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h index 1ed78be06c23..47fced159800 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index df0e9ffff706..c6a534303936 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -18,9 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -68,6 +65,8 @@ #include <linux/types.h> #include <linux/bitfield.h> +#include "iwl-trans.h" + /****************************/ /* Flow Handler Definitions */ /****************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index efb1998dcabd..4f10914f6048 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -14,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 5c8c0e130194..38085850a2d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -13,10 +13,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index 97072cf75bca..6fc8dac4aab7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -17,9 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 27db4a3ba1f8..ec300d388694 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 234d1009a9de..b7e1ddf8f177 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h index b49eda8150bb..cbd1a8eed620 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h @@ -8,6 +8,7 @@ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -18,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -35,6 +31,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c index b7cd813ba70f..ae83cfdb750e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h index d34de3f71db6..7020dca05221 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 421a869633a3..0f51c7bea8d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-scd.h b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h index 99b43da32adf..9f11f3912816 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-scd.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 7e9c924e1220..727f73e0b3f1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 279dd7b7a3fb..6c636b2a6b43 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -269,6 +264,7 @@ struct iwl_rx_cmd_buffer { bool _page_stolen; u32 _rx_page_order; unsigned int truesize; + u8 status; }; static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) @@ -538,9 +534,6 @@ struct iwl_trans_rxq_dma_data { * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last * TX'ed commands and similar. The buffer will be vfree'd by the caller. * Note that the transport must fill in the proper file headers. - * @dump_regs: dump using IWL_ERR configuration space and memory mapped - * registers of the device to diagnose failure, e.g., when HW becomes - * inaccessible. */ struct iwl_trans_ops { @@ -569,7 +562,7 @@ struct iwl_trans_ops { bool configure_scd); /* 22000 functions */ int (*txq_alloc)(struct iwl_trans *trans, - struct iwl_tx_queue_cfg_cmd *cmd, + __le16 flags, u8 sta_id, u8 tid, int cmd_id, int size, unsigned int queue_wdg_timeout); void (*txq_free)(struct iwl_trans *trans, int queue); @@ -611,8 +604,6 @@ struct iwl_trans_ops { struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans, const struct iwl_fw_dbg_trigger_tlv *trigger); - - void (*dump_regs)(struct iwl_trans *trans); }; /** @@ -688,6 +679,19 @@ enum iwl_plat_pm_mode { * enter/exit (in msecs). */ #define IWL_TRANS_IDLE_TIMEOUT 2000 +#define IWL_MAX_DEBUG_ALLOCATIONS 1 + +/** + * struct iwl_dram_data + * @physical: page phy pointer + * @block: pointer to the allocated block/page + * @size: size of the block/page + */ +struct iwl_dram_data { + dma_addr_t physical; + void *block; + int size; +}; /** * struct iwl_trans - transport common data @@ -722,6 +726,8 @@ enum iwl_plat_pm_mode { * @dbg_conf_tlv: array of pointers to configuration TLVs for debug * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv + * @num_blocks: number of blocks in fw_mon + * @fw_mon: address of the buffers for firmware monitor * @system_pm_mode: the system-wide power management mode in use. * This mode is set dynamically, depending on the WoWLAN values * configured from the userspace at runtime. @@ -773,6 +779,8 @@ struct iwl_trans { struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv; u32 dbg_dump_mask; u8 dbg_dest_reg_num; + int num_blocks; + struct iwl_dram_data fw_mon[IWL_MAX_DEBUG_ALLOCATIONS]; enum iwl_plat_pm_mode system_pm_mode; enum iwl_plat_pm_mode runtime_pm_mode; @@ -897,12 +905,6 @@ iwl_trans_dump_data(struct iwl_trans *trans, return trans->ops->dump_data(trans, trigger); } -static inline void iwl_trans_dump_regs(struct iwl_trans *trans) -{ - if (trans->ops->dump_regs) - trans->ops->dump_regs(trans); -} - static inline struct iwl_device_cmd * iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) { @@ -985,7 +987,7 @@ iwl_trans_txq_free(struct iwl_trans *trans, int queue) static inline int iwl_trans_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue_cfg_cmd *cmd, + __le16 flags, u8 sta_id, u8 tid, int cmd_id, int size, unsigned int wdg_timeout) { @@ -999,7 +1001,8 @@ iwl_trans_txq_alloc(struct iwl_trans *trans, return -EIO; } - return trans->ops->txq_alloc(trans, cmd, cmd_id, size, wdg_timeout); + return trans->ops->txq_alloc(trans, flags, sta_id, tid, + cmd_id, size, wdg_timeout); } static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index 75d35f6b041e..4094a4158032 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 016e03a5034f..730e37744dc0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -331,7 +326,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, struct ieee80211_chanctx_conf *chanctx_conf; /* default smps_mode is AUTOMATIC - only used for client modes */ enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; - u32 bt_activity_grading; + u32 bt_activity_grading, min_ag_for_static_smps; int ave_rssi; lockdep_assert_held(&mvm->mutex); @@ -363,8 +358,13 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, return; } + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2)) + min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC; + else + min_ag_for_static_smps = BT_HIGH_TRAFFIC; + bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); - if (bt_activity_grading >= BT_HIGH_TRAFFIC) + if (bt_activity_grading >= min_ag_for_static_smps) smps_mode = IEEE80211_SMPS_STATIC; else if (bt_activity_grading >= BT_LOW_TRAFFIC) smps_mode = IEEE80211_SMPS_DYNAMIC; @@ -691,6 +691,15 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, return bt_activity >= BT_LOW_TRAFFIC; } +u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants) +{ + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) && + (mvm->cfg->non_shared_ant & enabled_ants)) + return mvm->cfg->non_shared_ant; + + return first_antenna(enabled_ants); +} + u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, u8 ac) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index d61ff66ce07b..d96ada3c06fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 79bdae994822..210be26aadaa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -434,23 +434,13 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 chains_static, chains_dynamic; struct cfg80211_chan_def chandef; int ret, i; - struct iwl_binding_cmd binding_cmd = {}; + struct iwl_binding_cmd_v1 binding_cmd = {}; struct iwl_time_quota_cmd quota_cmd = {}; struct iwl_time_quota_data *quota; u32 status; - int size; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT)) { - size = sizeof(binding_cmd); - if (mvmvif->phy_ctxt->channel->band == NL80211_BAND_2GHZ || - !iwl_mvm_is_cdb_supported(mvm)) - binding_cmd.lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX); - else - binding_cmd.lmac_id = cpu_to_le32(IWL_LMAC_5G_INDEX); - } else { - size = IWL_BINDING_CMD_SIZE_V1; - } + + if (WARN_ON_ONCE(iwl_mvm_is_cdb_supported(mvm))) + return -EINVAL; /* add back the PHY */ if (WARN_ON(!mvmvif->phy_ctxt)) @@ -497,7 +487,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, status = 0; ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - size, &binding_cmd, &status); + IWL_BINDING_CMD_SIZE_V1, &binding_cmd, + &status); if (ret) { IWL_ERR(mvm, "Failed to add binding: %d\n", ret); return ret; @@ -1042,7 +1033,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, * the recording automatically before entering D3. This can * be removed once the FW starts doing that. */ - iwl_fw_dbg_stop_recording(&mvm->fwrt); + _iwl_fw_dbg_stop_recording(mvm->fwrt.trans, NULL); /* must be last -- this switches firmware state */ ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); @@ -1362,7 +1353,7 @@ static void iwl_mvm_set_key_rx_seq(struct iwl_mvm *mvm, struct ieee80211_key_conf *key, struct iwl_wowlan_status *status) { - union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; + union iwl_all_tsc_rsc *rsc = &status->gtk[0].rsc.all_tsc_rsc; switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: @@ -1419,7 +1410,8 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, */ if (sta) { struct ieee80211_key_seq seq = {}; - union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; + union iwl_all_tsc_rsc *sc = + &data->status->gtk[0].rsc.all_tsc_rsc; if (data->find_phase) return; @@ -1501,22 +1493,24 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, u8 key[32]; } conf = { .conf.cipher = gtkdata.cipher, - .conf.keyidx = status->gtk.key_index, + .conf.keyidx = + iwlmvm_wowlan_gtk_idx(&status->gtk[0]), }; + __be64 replay_ctr; switch (gtkdata.cipher) { case WLAN_CIPHER_SUITE_CCMP: conf.conf.keylen = WLAN_KEY_LEN_CCMP; - memcpy(conf.conf.key, status->gtk.decrypt_key, + memcpy(conf.conf.key, status->gtk[0].key, WLAN_KEY_LEN_CCMP); break; case WLAN_CIPHER_SUITE_TKIP: conf.conf.keylen = WLAN_KEY_LEN_TKIP; - memcpy(conf.conf.key, status->gtk.decrypt_key, 16); + memcpy(conf.conf.key, status->gtk[0].key, 16); /* leave TX MIC key zeroed, we don't use it anyway */ memcpy(conf.conf.key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, - status->gtk.tkip_mic_key, 8); + status->gtk[0].tkip_mic_key, 8); break; } @@ -1524,11 +1518,10 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, if (IS_ERR(key)) return false; iwl_mvm_set_key_rx_seq(mvm, key, status); - } - if (status->num_of_gtk_rekeys) { - __be64 replay_ctr = + replay_ctr = cpu_to_be64(le64_to_cpu(status->replay_ctr)); + ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, (void *)&replay_ctr, GFP_KERNEL); } @@ -1541,6 +1534,107 @@ out: return true; } +struct iwl_wowlan_status *iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm) +{ + struct iwl_wowlan_status *v7, *status; + struct iwl_host_cmd cmd = { + .id = WOWLAN_GET_STATUSES, + .flags = CMD_WANT_SKB, + }; + int ret, len, status_size; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) { + IWL_ERR(mvm, "failed to query wakeup status (%d)\n", ret); + return ERR_PTR(ret); + } + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL)) { + struct iwl_wowlan_status_v6 *v6 = (void *)cmd.resp_pkt->data; + int data_size; + + status_size = sizeof(*v6); + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + + if (len < status_size) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + status = ERR_PTR(-EIO); + goto out_free_resp; + } + + data_size = ALIGN(le32_to_cpu(v6->wake_packet_bufsize), 4); + + if (len != (status_size + data_size)) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + status = ERR_PTR(-EIO); + goto out_free_resp; + } + + status = kzalloc(sizeof(*status) + data_size, GFP_KERNEL); + if (!status) + goto out_free_resp; + + BUILD_BUG_ON(sizeof(v6->gtk.decrypt_key) > + sizeof(status->gtk[0].key)); + BUILD_BUG_ON(sizeof(v6->gtk.tkip_mic_key) > + sizeof(status->gtk[0].tkip_mic_key)); + + /* copy GTK info to the right place */ + memcpy(status->gtk[0].key, v6->gtk.decrypt_key, + sizeof(v6->gtk.decrypt_key)); + memcpy(status->gtk[0].tkip_mic_key, v6->gtk.tkip_mic_key, + sizeof(v6->gtk.tkip_mic_key)); + memcpy(&status->gtk[0].rsc, &v6->gtk.rsc, + sizeof(status->gtk[0].rsc)); + + /* hardcode the key length to 16 since v6 only supports 16 */ + status->gtk[0].key_len = 16; + + /* + * The key index only uses 2 bits (values 0 to 3) and + * we always set bit 7 which means this is the + * currently used key. + */ + status->gtk[0].key_flags = v6->gtk.key_index | BIT(7); + + status->replay_ctr = v6->replay_ctr; + + /* everything starting from pattern_number is identical */ + memcpy(&status->pattern_number, &v6->pattern_number, + offsetof(struct iwl_wowlan_status, wake_packet) - + offsetof(struct iwl_wowlan_status, pattern_number) + + data_size); + + goto out_free_resp; + } + + v7 = (void *)cmd.resp_pkt->data; + status_size = sizeof(*v7); + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + + if (len < status_size) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + status = ERR_PTR(-EIO); + goto out_free_resp; + } + + if (len != (status_size + + ALIGN(le32_to_cpu(v7->wake_packet_bufsize), 4))) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + status = ERR_PTR(-EIO); + goto out_free_resp; + } + + status = kmemdup(v7, len, GFP_KERNEL); + +out_free_resp: + iwl_free_resp(&cmd); + return status; +} + static struct iwl_wowlan_status * iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -1550,12 +1644,7 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) u32 valid; u32 error_id; } err_info; - struct iwl_host_cmd cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_WANT_SKB, - }; - struct iwl_wowlan_status *status, *fw_status; - int ret, len, status_size; + int ret; iwl_trans_read_mem_bytes(mvm->trans, base, &err_info, sizeof(err_info)); @@ -1578,34 +1667,7 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (ret) IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, "failed to query status (%d)\n", ret); - return ERR_PTR(ret); - } - - status_size = sizeof(*fw_status); - - len = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (len < status_size) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - fw_status = ERR_PTR(-EIO); - goto out_free_resp; - } - - status = (void *)cmd.resp_pkt->data; - if (len != (status_size + - ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - fw_status = ERR_PTR(-EIO); - goto out_free_resp; - } - - fw_status = kmemdup(status, len, GFP_KERNEL); - -out_free_resp: - iwl_free_resp(&cmd); - return fw_status; + return iwl_mvm_send_wowlan_get_status(mvm); } /* releases the MVM mutex */ @@ -1883,6 +1945,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) goto err; } + iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); /* query SRAM first in case we want event logging */ iwl_mvm_read_d3_sram(mvm); @@ -2117,6 +2180,8 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) mvm->d3_test_active = false; + iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); + rtnl_lock(); __iwl_mvm_resume(mvm, true); rtnl_unlock(); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index 798605c4f122..1aa6c7e93088 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 05b77419953c..de40752aa67e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -1733,6 +1728,35 @@ iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, } static ssize_t +iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_he_monitor_cmd he_mon_cmd = {}; + u32 aid; + int ret; + + if (!iwl_mvm_firmware_running(mvm)) + return -EIO; + + ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid, + &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1], + &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3], + &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]); + if (ret != 7) + return -EINVAL; + + he_mon_cmd.aid = cpu_to_le16(aid); + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD, + DATA_PATH_GROUP, 0), 0, + sizeof(he_mon_cmd), &he_mon_cmd); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_uapsd_noagg_bssids_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1801,6 +1825,8 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); MVM_DEBUGFS_READ_FILE_OPS(sar_geo_profile); #endif +MVM_DEBUGFS_WRITE_FILE_OPS(he_sniffer_params, 32); + static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1989,6 +2015,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) #ifdef CONFIG_ACPI MVM_DEBUGFS_ADD_FILE(sar_geo_profile, dbgfs_dir, 0400); #endif + MVM_DEBUGFS_ADD_FILE(he_sniffer_params, mvm->debugfs_dir, 0200); if (!debugfs_create_bool("enable_scan_iteration_notif", 0600, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h index ede6ef8d390e..a83d252c0602 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index e8e74dd558f7..143c7fcaea41 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6bb1a99a197a..96d26b749952 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -773,19 +768,28 @@ out_free: int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { - struct iwl_dev_tx_power_cmd cmd = { - .v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), - }; + union { + struct iwl_dev_tx_power_cmd v5; + struct iwl_dev_tx_power_cmd_v4 v4; + } cmd; int i, j, idx; int profs[ACPI_SAR_NUM_CHAIN_LIMITS] = { prof_a, prof_b }; - int len = sizeof(cmd); + int len; BUILD_BUG_ON(ACPI_SAR_NUM_CHAIN_LIMITS < 2); BUILD_BUG_ON(ACPI_SAR_NUM_CHAIN_LIMITS * ACPI_SAR_NUM_SUB_BANDS != ACPI_SAR_TABLE_SIZE); - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) - len = sizeof(cmd.v3); + cmd.v5.v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS); + + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_REDUCE_TX_POWER)) + len = sizeof(cmd.v5); + else if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) + len = sizeof(cmd.v4); + else + len = sizeof(cmd.v4.v3); for (i = 0; i < ACPI_SAR_NUM_CHAIN_LIMITS; i++) { struct iwl_mvm_sar_profile *prof; @@ -812,7 +816,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS; j++) { idx = (i * ACPI_SAR_NUM_SUB_BANDS) + j; - cmd.v3.per_chain_restriction[i][j] = + cmd.v5.v3.per_chain_restriction[i][j] = cpu_to_le16(prof->table[idx]); IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n", j, prof->table[idx]); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index b27269504a62..9bb1de1cad64 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index b3fd20502abb..781f30356720 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -18,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -35,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -85,6 +82,10 @@ const u8 iwl_mvm_ac_to_gen2_tx_fifo[] = { IWL_GEN2_EDCA_TX_FIFO_VI, IWL_GEN2_EDCA_TX_FIFO_BE, IWL_GEN2_EDCA_TX_FIFO_BK, + IWL_GEN2_TRIG_TX_FIFO_VO, + IWL_GEN2_TRIG_TX_FIFO_VI, + IWL_GEN2_TRIG_TX_FIFO_BE, + IWL_GEN2_TRIG_TX_FIFO_BK, }; struct iwl_mvm_mac_iface_iterator_data { @@ -1568,6 +1569,65 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, ieee80211_rx_napi(mvm->hw, NULL, skb, NULL); } +static void iwl_mvm_probe_resp_data_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_probe_resp_data_notif *notif = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_probe_resp_data *old_data, *new_data; + + if (mvmvif->id != (u16)le32_to_cpu(notif->mac_id)) + return; + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) + return; + + memcpy(&new_data->notif, notif, sizeof(new_data->notif)); + + /* noa_attr contains 1 reserved byte, need to substruct it */ + new_data->noa_len = sizeof(struct ieee80211_vendor_ie) + + sizeof(new_data->notif.noa_attr) - 1; + + /* + * If it's a one time NoA, only one descriptor is needed, + * adjust the length according to len_low. + */ + if (new_data->notif.noa_attr.len_low == + sizeof(struct ieee80211_p2p_noa_desc) + 2) + new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc); + + old_data = rcu_dereference_protected(mvmvif->probe_resp_data, + lockdep_is_held(&mvmvif->mvm->mutex)); + rcu_assign_pointer(mvmvif->probe_resp_data, new_data); + + if (old_data) + kfree_rcu(old_data, rcu_head); + + if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA && + notif->csa_counter >= 1) + ieee80211_csa_set_counter(vif, notif->csa_counter); +} + +void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_probe_resp_data_notif *notif = (void *)pkt->data; + int len = iwl_rx_packet_payload_len(pkt); + + if (WARN_ON_ONCE(len < sizeof(*notif))) + return; + + IWL_DEBUG_INFO(mvm, "Probe response data notif: noa %d, csa %d\n", + notif->noa_active, notif->csa_counter); + + ieee80211_iterate_active_interfaces(mvm->hw, + IEEE80211_IFACE_ITER_ACTIVE, + iwl_mvm_probe_resp_data_iter, + notif); +} + void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d46f3fbea46e..c78d017749d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -559,8 +554,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_remain_on_channel_duration = 10000; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - /* we can compensate an offset of up to 3 channels = 15 MHz */ - hw->wiphy->max_adj_channel_rssi_comp = 3 * 5; /* Extract MAC address */ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); @@ -1035,6 +1028,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, mvmvif->phy_ctxt = NULL; memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); + memset(&mvmvif->probe_resp_data, 0, sizeof(mvmvif->probe_resp_data)); } static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) @@ -1124,7 +1118,9 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm) * would do. */ clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); +#ifdef CONFIG_PM iwl_mvm_d0i3_enable_tx(mvm, NULL); +#endif } return ret; @@ -1162,7 +1158,9 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) mutex_lock(&mvm->mutex); clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); +#ifdef CONFIG_PM iwl_mvm_d0i3_enable_tx(mvm, NULL); +#endif ret = iwl_mvm_update_quotas(mvm, true, NULL); if (ret) IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", @@ -1308,19 +1306,28 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, s16 tx_power) { - struct iwl_dev_tx_power_cmd cmd = { - .v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), - .v3.mac_context_id = + int len; + union { + struct iwl_dev_tx_power_cmd v5; + struct iwl_dev_tx_power_cmd_v4 v4; + } cmd = { + .v5.v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), + .v5.v3.mac_context_id = cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), - .v3.pwr_restriction = cpu_to_le16(8 * tx_power), + .v5.v3.pwr_restriction = cpu_to_le16(8 * tx_power), }; - int len = sizeof(cmd); if (tx_power == IWL_DEFAULT_MAX_TX_POWER) - cmd.v3.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); + cmd.v5.v3.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) - len = sizeof(cmd.v3); + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_REDUCE_TX_POWER)) + len = sizeof(cmd.v5); + else if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) + len = sizeof(cmd.v4); + else + len = sizeof(cmd.v4.v3); return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); } @@ -1333,6 +1340,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, int ret; mvmvif->mvm = mvm; + RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL); /* * make sure D0i3 exit is completed, otherwise a target access @@ -1497,6 +1505,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_probe_resp_data *probe_data; iwl_mvm_prepare_mac_removal(mvm, vif); @@ -1506,6 +1515,12 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + probe_data = rcu_dereference_protected(mvmvif->probe_resp_data, + lockdep_is_held(&mvm->mutex)); + RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL); + if (probe_data) + kfree_rcu(probe_data, rcu_head); + if (mvm->bf_allowed_vif == mvmvif) { mvm->bf_allowed_vif = NULL; vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | @@ -2455,6 +2470,9 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_remove(mvm, vif); + kfree(mvmvif->ap_wep_key); + mvmvif->ap_wep_key = NULL; + mutex_unlock(&mvm->mutex); } @@ -2927,7 +2945,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } - iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band); + iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, + false); ret = iwl_mvm_update_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { @@ -2943,9 +2962,16 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, /* enable beacon filtering */ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band); + iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, + true); - ret = 0; + /* if wep is used, need to set the key for the station now */ + if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) + ret = iwl_mvm_set_sta_key(mvm, vif, sta, + mvmvif->ap_wep_key, + STA_KEY_IDX_INVALID); + else + ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { /* disable beacon filtering */ @@ -3128,8 +3154,15 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + if (!mvm->trans->cfg->gen2) { + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + } else if (vif->type == NL80211_IFTYPE_STATION) { + key->flags |= IEEE80211_KEY_FLAG_PUT_MIC_SPACE; + } else { + IWL_DEBUG_MAC80211(mvm, "Use SW encryption for TKIP\n"); + return -EOPNOTSUPP; + } break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_GCMP: @@ -3144,13 +3177,17 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: - /* For non-client mode, only use WEP keys for TX as we probably - * don't have a station yet anyway and would then have to keep - * track of the keys, linking them to each of the clients/peers - * as they appear. For now, don't do that, for performance WEP - * offload doesn't really matter much, but we need it for some - * other offload features in client mode. - */ + if (vif->type == NL80211_IFTYPE_AP) { + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(vif); + + mvmvif->ap_wep_key = kmemdup(key, + sizeof(*key) + key->keylen, + GFP_KERNEL); + if (!mvmvif->ap_wep_key) + return -ENOMEM; + } + if (vif->type != NL80211_IFTYPE_STATION) return 0; break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index b3987a0a7018..8f71eeed50d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -336,6 +331,18 @@ struct iwl_mvm_vif_bf_data { }; /** + * struct iwl_probe_resp_data - data for NoA/CSA updates + * @rcu_head: used for freeing the data on update + * @notif: notification data + * @noa_len: length of NoA attribute, calculated from the notification + */ +struct iwl_probe_resp_data { + struct rcu_head rcu_head; + struct iwl_probe_resp_data_notif notif; + int noa_len; +}; + +/** * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context * @id: between 0 and 3 * @color: to solve races upon MAC addition and removal @@ -365,6 +372,8 @@ struct iwl_mvm_vif_bf_data { * average signal of beacons retrieved from the firmware * @csa_failed: CSA failed to schedule time event, report an error later * @features: hw features active for this vif + * @probe_resp_data: data from FW notification to store NOA and CSA related + * data to be inserted into probe response. */ struct iwl_mvm_vif { struct iwl_mvm *mvm; @@ -460,6 +469,9 @@ struct iwl_mvm_vif { /* TCP Checksum Offload */ netdev_features_t features; + + struct iwl_probe_resp_data __rcu *probe_resp_data; + struct ieee80211_key_conf *ap_wep_key; }; static inline struct iwl_mvm_vif * @@ -1229,6 +1241,11 @@ static inline bool iwl_mvm_is_oce_supported(struct iwl_mvm *mvm) return fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_OCE); } +static inline bool iwl_mvm_is_frag_ebs_supported(struct iwl_mvm *mvm) +{ + return fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAG_EBS); +} + static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) { /* For now we only use this mode to differentiate between @@ -1602,6 +1619,8 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, struct ieee80211_vif *exclude_vif); +void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); /* Bindings */ @@ -1685,7 +1704,7 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) #endif /* CONFIG_IWLWIFI_DEBUGFS */ /* rate scaling */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool sync); void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg); int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate); void rs_update_last_rssi(struct iwl_mvm *mvm, @@ -1733,6 +1752,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); extern const struct file_operations iwl_dbgfs_d3_test_ops; +struct iwl_wowlan_status *iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm); #ifdef CONFIG_PM int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -1776,10 +1796,13 @@ void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); bool iwl_mvm_ref_taken(struct iwl_mvm *mvm); + +#ifdef CONFIG_PM void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode); int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode); int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); +#endif /* BT Coex */ int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm); @@ -1796,6 +1819,7 @@ bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant); bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm); bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum nl80211_band band); +u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants); u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, u8 ac); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index cf48517944ec..fff98fed35ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c index 6338d9cf7070..6d71e05626ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 0e26619fb330..0599d323cbeb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -182,6 +177,9 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) if (mvm->trans->cfg->device_family < IWL_DEVICE_FAMILY_8000) reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; + if (iwl_fw_dbg_is_d3_debug_enabled(&mvm->fwrt)) + reg_val |= CSR_HW_IF_CONFIG_REG_D3_DEBUG; + iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | @@ -189,7 +187,8 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI | + CSR_HW_IF_CONFIG_REG_D3_DEBUG, reg_val); IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, @@ -491,7 +490,9 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = { /* this forward declaration can avoid to export the function */ static void iwl_mvm_async_handlers_wk(struct work_struct *wk); +#ifdef CONFIG_PM static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); +#endif static u32 iwl_mvm_min_backoff(struct iwl_mvm *mvm) { @@ -583,6 +584,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, }; int err, scan_size; u32 min_backoff; + enum iwl_amsdu_size rb_size_default; /* * We use IWL_MVM_STATION_COUNT to check the validity of the station @@ -661,7 +663,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); +#ifdef CONFIG_PM INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); +#endif INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk); INIT_WORK(&mvm->add_stream_wk, iwl_mvm_add_new_dqa_stream_wk); @@ -691,8 +695,16 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + + if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) + rb_size_default = IWL_AMSDU_2K; + else + rb_size_default = IWL_AMSDU_4K; + switch (iwlwifi_mod_params.amsdu_size) { case IWL_AMSDU_DEF: + trans_cfg.rx_buf_size = rb_size_default; + break; case IWL_AMSDU_4K: trans_cfg.rx_buf_size = IWL_AMSDU_4K; break; @@ -705,16 +717,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, default: pr_err("%s: Unsupported amsdu_size: %d\n", KBUILD_MODNAME, iwlwifi_mod_params.amsdu_size); - trans_cfg.rx_buf_size = IWL_AMSDU_4K; - } - - /* the hardware splits the A-MSDU */ - if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) { - trans_cfg.rx_buf_size = IWL_AMSDU_2K; - /* TODO: remove when balanced power mode is fw supported */ - iwlmvm_mod_params.power_scheme = IWL_POWER_SCHEME_CAM; - } else if (mvm->cfg->mq_rx_supported) { - trans_cfg.rx_buf_size = IWL_AMSDU_4K; + trans_cfg.rx_buf_size = rb_size_default; } trans->wide_cmd_header = true; @@ -1246,7 +1249,8 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); schedule_work(&reprobe->work); } else if (mvm->fwrt.cur_fw_img == IWL_UCODE_REGULAR && - mvm->hw_registered) { + mvm->hw_registered && + !test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { /* don't let the transport/FW power down */ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); @@ -1261,7 +1265,8 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - iwl_mvm_dump_nic_error_log(mvm); + if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) + iwl_mvm_dump_nic_error_log(mvm); iwl_mvm_nic_restart(mvm, true); } @@ -1274,6 +1279,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) iwl_mvm_nic_restart(mvm, true); } +#ifdef CONFIG_PM struct iwl_d0i3_iter_data { struct iwl_mvm *mvm; struct ieee80211_vif *connected_vif; @@ -1596,25 +1602,23 @@ out: static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) { struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); - struct iwl_host_cmd get_status_cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, - }; struct iwl_mvm_d0i3_exit_work_iter_data iter_data = { .mvm = mvm, }; struct iwl_wowlan_status *status; - int ret; u32 wakeup_reasons = 0; __le16 *qos_seq = NULL; mutex_lock(&mvm->mutex); - ret = iwl_mvm_send_cmd(mvm, &get_status_cmd); - if (ret) + + status = iwl_mvm_send_wowlan_get_status(mvm); + if (IS_ERR_OR_NULL(status)) { + /* set to NULL so we don't need to check before kfree'ing */ + status = NULL; goto out; + } - status = (void *)get_status_cmd.resp_pkt->data; wakeup_reasons = le32_to_cpu(status->wakeup_reasons); qos_seq = status->qos_seq_ctr; @@ -1633,8 +1637,7 @@ out: wakeup_reasons); /* qos_seq might point inside resp_pkt, so free it only now */ - if (get_status_cmd.resp_pkt) - iwl_free_resp(&get_status_cmd); + kfree(status); /* the FW might have updated the regdomain */ iwl_mvm_update_changed_regdom(mvm); @@ -1685,6 +1688,13 @@ int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) return _iwl_mvm_exit_d0i3(mvm); } +#define IWL_MVM_D0I3_OPS \ + .enter_d0i3 = iwl_mvm_enter_d0i3, \ + .exit_d0i3 = iwl_mvm_exit_d0i3, +#else /* CONFIG_PM */ +#define IWL_MVM_D0I3_OPS +#endif /* CONFIG_PM */ + #define IWL_MVM_COMMON_OPS \ /* these could be differentiated */ \ .async_cb = iwl_mvm_async_cb, \ @@ -1695,8 +1705,7 @@ int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) .nic_error = iwl_mvm_nic_error, \ .cmd_queue_full = iwl_mvm_cmd_queue_full, \ .nic_config = iwl_mvm_nic_config, \ - .enter_d0i3 = iwl_mvm_enter_d0i3, \ - .exit_d0i3 = iwl_mvm_exit_d0i3, \ + IWL_MVM_D0I3_OPS \ /* as we only register one, these MUST be common! */ \ .start = iwl_op_mode_mvm_start, \ .stop = iwl_op_mode_mvm_stop diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index c11fe2621d51..5a0a28fd762d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c index 690559bdf421..5e62b97af48b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 8169d1450b3b..7a98e1a1dc40 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -117,20 +117,42 @@ static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm, { struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; bool vht_ena = vht_cap && vht_cap->vht_supported; u16 flags = 0; 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; + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) { + if (he_cap && he_cap->has_he) { + if (he_cap->he_cap_elem.phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + + if (he_cap->he_cap_elem.phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ) + flags |= IWL_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK; + } else if ((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 (he_cap && he_cap->has_he && + (he_cap->he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) { + flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; + + if (he_cap->he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2) + flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK; + } + return flags; } @@ -311,7 +333,7 @@ out: } void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band) + enum nl80211_band band, bool update) { struct ieee80211_hw *hw = mvm->hw; struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); @@ -320,7 +342,8 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_supported_band *sband; struct iwl_tlc_config_cmd cfg_cmd = { .sta_id = mvmsta->sta_id, - .max_ch_width = rs_fw_bw_from_sta_bw(sta), + .max_ch_width = update ? + rs_fw_bw_from_sta_bw(sta) : RATE_MCS_CHAN_WIDTH_20, .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_mpdu_len = cpu_to_le16(sta->max_amsdu_len), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 30cfd7d50bc9..2c75f51a04e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1276,7 +1276,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, (unsigned long)(lq_sta->last_tx + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); - iwl_mvm_rs_rate_init(mvm, sta, info->band); + iwl_mvm_rs_rate_init(mvm, sta, info->band, true); return; } lq_sta->last_tx = jiffies; @@ -2859,9 +2859,8 @@ void rs_update_last_rssi(struct iwl_mvm *mvm, static void rs_initialize_lq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - enum nl80211_band band) + enum nl80211_band band, bool update) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_scale_tbl_info *tbl; struct rs_rate *rate; u8 active_tbl = 0; @@ -2890,8 +2889,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_lq_cmd(mvm, sta, lq_sta, rate); /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, - mvmsta->sta_state < IEEE80211_STA_AUTHORIZED); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, !update); } static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, @@ -3144,7 +3142,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) * Called after adding a new station to initialize rate scaling */ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band) + enum nl80211_band band, bool update) { int i, j; struct ieee80211_hw *hw = mvm->hw; @@ -3215,7 +3213,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = - first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); + iwl_mvm_bt_coex_get_single_ant_msk(mvm, iwl_mvm_get_valid_tx_ant(mvm)); lq_sta->lq.dual_stream_ant_msk = ANT_AB; /* as default allow aggregation for all tids */ @@ -3224,7 +3222,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, #ifdef CONFIG_IWLWIFI_DEBUGFS iwl_mvm_reset_frame_stats(mvm); #endif - rs_initialize_lq(mvm, sta, lq_sta, band); + rs_initialize_lq(mvm, sta, lq_sta, band, update); } static void rs_drv_rate_update(void *mvm_r, @@ -3244,7 +3242,7 @@ static void rs_drv_rate_update(void *mvm_r, for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) ieee80211_stop_tx_ba_session(sta, tid); - iwl_mvm_rs_rate_init(mvm, sta, sband->band); + iwl_mvm_rs_rate_init(mvm, sta, sband->band, true); } #ifdef CONFIG_MAC80211_DEBUGFS @@ -3578,7 +3576,8 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - if (num_of_ant(initial_rate->ant) == 1) + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) && + num_of_ant(initial_rate->ant) == 1) lq_cmd->single_stream_ant_msk = initial_rate->ant; lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; @@ -4098,12 +4097,12 @@ static const struct rate_control_ops rs_mvm_ops_drv = { }; void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band) + enum nl80211_band band, bool update) { if (iwl_mvm_has_tlc_offload(mvm)) - rs_fw_rate_init(mvm, sta, band); + rs_fw_rate_init(mvm, sta, band, update); else - rs_drv_rate_init(mvm, sta, band); + rs_drv_rate_init(mvm, sta, band, update); } int iwl_mvm_rate_control_register(void) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index d2cf484e2b73..d0f47899f284 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -420,7 +420,7 @@ struct iwl_lq_sta { /* Initialize station's rate scaling information after adding station */ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band); + enum nl80211_band band, bool init); /* Notify RS about Tx status */ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, @@ -461,7 +461,7 @@ void rs_remove_sta_debugfs(void *mvm, void *mvm_sta); 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); + enum nl80211_band band, bool update); 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, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index bfb163419c67..a050220da678 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index b53148f972a4..894dd6379b9a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -283,6 +283,10 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, !(status & IWL_RX_MPDU_RES_STATUS_TTAK_OK)) return 0; + if (mvm->trans->cfg->gen2 && + !(status & RX_MPDU_RES_STATUS_MIC_OK)) + stats->flag |= RX_FLAG_MMIC_ERROR; + *crypt_len = IEEE80211_TKIP_IV_LEN; /* fall through if TTAK OK */ case IWL_RX_MPDU_STATUS_SEC_WEP: @@ -294,8 +298,11 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, IWL_RX_MPDU_STATUS_SEC_WEP) *crypt_len = IEEE80211_WEP_IV_LEN; - if (pkt_flags & FH_RSCSR_RADA_EN) + if (pkt_flags & FH_RSCSR_RADA_EN) { stats->flag |= RX_FLAG_ICV_STRIPPED; + if (mvm->trans->cfg->gen2) + stats->flag |= RX_FLAG_MMIC_STRIPPED; + } return 0; case IWL_RX_MPDU_STATUS_SEC_EXT_ENC: @@ -856,6 +863,405 @@ static void iwl_mvm_flip_address(u8 *addr) ether_addr_copy(addr, mac_addr); } +static void iwl_mvm_decode_he_sigb(struct iwl_mvm *mvm, + struct iwl_rx_mpdu_desc *desc, + u32 rate_n_flags, + struct ieee80211_radiotap_he_mu *he_mu) +{ + u32 sigb0, sigb1; + u16 sigb2; + + if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) { + sigb0 = le32_to_cpu(desc->v3.sigb_common0); + sigb1 = le32_to_cpu(desc->v3.sigb_common1); + } else { + sigb0 = le32_to_cpu(desc->v1.sigb_common0); + sigb1 = le32_to_cpu(desc->v1.sigb_common1); + } + + sigb2 = le16_to_cpu(desc->sigb_common2); + + if (FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH1_CRC_OK, sigb2)) { + he_mu->flags1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN); + + he_mu->flags1 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH1_CTR_RU, + sigb2), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU); + + he_mu->ru_ch1[0] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH1_RU0, + sigb0); + he_mu->ru_ch1[1] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH1_RU1, + sigb1); + he_mu->ru_ch1[2] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH1_RU2, + sigb0); + he_mu->ru_ch1[3] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH1_RU3, + sigb1); + } + + if (FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH2_CRC_OK, sigb2) && + (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) != RATE_MCS_CHAN_WIDTH_20) { + he_mu->flags1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN); + + he_mu->flags2 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_SIGB_COMMON2_CH2_CTR_RU, + sigb2), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU); + + he_mu->ru_ch2[0] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH2_RU0, + sigb0); + he_mu->ru_ch2[1] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH2_RU1, + sigb1); + he_mu->ru_ch2[2] = FIELD_GET(IWL_RX_HE_SIGB_COMMON0_CH2_RU2, + sigb0); + he_mu->ru_ch2[3] = FIELD_GET(IWL_RX_HE_SIGB_COMMON1_CH2_RU3, + sigb1); + } +} + +static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb, + struct iwl_rx_mpdu_desc *desc, + u32 rate_n_flags, u16 phy_info, int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + /* this is invalid e.g. because puncture type doesn't allow 0b11 */ +#define HE_PHY_DATA_INVAL ((u64)-1) + u64 he_phy_data = HE_PHY_DATA_INVAL; + struct ieee80211_radiotap_he *he = NULL; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + u32 he_type = 0xffffffff; + u8 stbc, ltf; + + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN), + }; + static const struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN), + .flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + unsigned int radiotap_len = 0; + bool overload = phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD; + bool sigb_data = false; + + he = skb_put_data(skb, &known, sizeof(known)); + radiotap_len += sizeof(known); + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + + he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) { + if (mvm->trans->cfg->device_family >= + IWL_DEVICE_FAMILY_22560) + he_phy_data = le64_to_cpu(desc->v3.he_phy_data); + else + he_phy_data = le64_to_cpu(desc->v1.he_phy_data); + + if (he_type == RATE_MCS_HE_TYPE_MU) { + he_mu = skb_put_data(skb, &mu_known, + sizeof(mu_known)); + radiotap_len += sizeof(mu_known); + rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; + } + } + + /* temporarily hide the radiotap data */ + __skb_pull(skb, radiotap_len); + + if (overload && he_type == RATE_MCS_HE_TYPE_SU) { + he->data1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN); + if (FIELD_GET(IWL_RX_HE_PHY_UPLINK, he_phy_data)) + he->data3 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (FIELD_GET(IWL_RX_HE_PHY_DELIM_EOF, he_phy_data)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + } else if (overload && he_mu && he_phy_data != HE_PHY_DATA_INVAL) { + he_mu->flags1 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIBG_SYM_OR_USER_NUM_MASK, + he_phy_data), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); + he_mu->flags1 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIGB_DCM, + he_phy_data), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); + he_mu->flags1 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIGB_MCS_MASK, + he_phy_data), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); + he_mu->flags2 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_SIGB_COMPRESSION, + he_phy_data), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); + he_mu->flags2 |= + le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_MU_PREAMBLE_PUNC_TYPE_MASK, + he_phy_data), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); + + sigb_data = FIELD_GET(IWL_RX_HE_PHY_INFO_TYPE_MASK, + he_phy_data) == + IWL_RX_HE_PHY_INFO_TYPE_MU_EXT_INFO; + if (sigb_data) + iwl_mvm_decode_he_sigb(mvm, desc, rate_n_flags, he_mu); + } + if (he_phy_data != HE_PHY_DATA_INVAL && + (he_type == RATE_MCS_HE_TYPE_SU || + he_type == RATE_MCS_HE_TYPE_MU)) { + u8 bss_color = FIELD_GET(IWL_RX_HE_PHY_BSS_COLOR_MASK, + he_phy_data); + + if (bss_color) { + he->data1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN); + he->data3 |= cpu_to_le16(bss_color); + } + } + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; + + /* toggle is switched whenever new aggregation starts */ + if (toggle_bit != mvm->ampdu_toggle && + he_phy_data != HE_PHY_DATA_INVAL && + (he_type == RATE_MCS_HE_TYPE_MU || + he_type == RATE_MCS_HE_TYPE_SU)) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (FIELD_GET(IWL_RX_HE_PHY_DELIM_EOF, + he_phy_data)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + } + + if (he_type == RATE_MCS_HE_TYPE_EXT_SU && + rate_n_flags & RATE_MCS_HE_106T_MSK) { + rx_status->bw = RATE_INFO_BW_HE_RU; + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + } + + if (he_phy_data != HE_PHY_DATA_INVAL && + (FIELD_GET(IWL_RX_HE_PHY_INFO_TYPE_MASK, he_phy_data) == + IWL_RX_HE_PHY_INFO_TYPE_MU_EXT_INFO || + FIELD_GET(IWL_RX_HE_PHY_INFO_TYPE_MASK, he_phy_data) == + IWL_RX_HE_PHY_INFO_TYPE_TB_EXT_INFO)) { + /* + * Unfortunately, we have to leave the mac80211 data + * incorrect for the case that we receive an HE-MU + * transmission and *don't* have the HE phy data (due + * to the bits being used for TSF). This shouldn't + * happen though as management frames where we need + * the TSF/timers are not be transmitted in HE-MU. + */ + u8 ru = FIELD_GET(IWL_RX_HE_PHY_RU_ALLOC_MASK, he_phy_data); + u8 offs = 0; + + rx_status->bw = RATE_INFO_BW_HE_RU; + + he->data1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + + switch (ru) { + case 0 ... 36: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + he->data2 |= + le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); + he->data2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN); + if (he_phy_data & IWL_RX_HE_PHY_RU_ALLOC_SEC80) + he->data2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC); + + if (he_mu) { +#define CHECK_BW(bw) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \ + RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS) + CHECK_BW(20); + CHECK_BW(40); + CHECK_BW(80); + CHECK_BW(160); + he->data2 |= + le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, + rate_n_flags), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); + } + } else if (he_type == RATE_MCS_HE_TYPE_SU || + he_type == RATE_MCS_HE_TYPE_EXT_SU) { + he->data1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + } + + stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> RATE_MCS_STBC_POS; + rx_status->nss = + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; + rx_status->encoding = RX_ENC_HE; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; + if (rate_n_flags & RATE_MCS_BF_MSK) + rx_status->enc_flags |= RX_ENC_FLAG_BF; + + rx_status->he_dcm = + !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); + +#define CHECK_TYPE(F) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ + (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + + CHECK_TYPE(SU); + CHECK_TYPE(EXT_SU); + CHECK_TYPE(MU); + CHECK_TYPE(TRIG); + + he->data1 |= cpu_to_le16(he_type >> RATE_MCS_HE_TYPE_POS); + + if (rate_n_flags & RATE_MCS_BF_MSK) + he->data5 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA5_TXBF); + + switch ((rate_n_flags & RATE_MCS_HE_GI_LTF_MSK) >> + RATE_MCS_HE_GI_LTF_POS) { + case 0: + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + if (he_type == RATE_MCS_HE_TYPE_MU) + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + else + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; + break; + case 1: + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + break; + case 2: + if (he_type == RATE_MCS_HE_TYPE_TRIG) { + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + } else { + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + } + break; + case 3: + if ((he_type == RATE_MCS_HE_TYPE_SU || + he_type == RATE_MCS_HE_TYPE_EXT_SU) && + rate_n_flags & RATE_MCS_SGI_MSK) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + break; + } + + he->data5 |= le16_encode_bits(ltf, IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); + + switch (he_type) { + case RATE_MCS_HE_TYPE_SU: + case RATE_MCS_HE_TYPE_EXT_SU: { + u16 val; + + /* LTF syms correspond to streams */ + he->data2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN); + switch (rx_status->nss) { + case 1: + val = 0; + break; + case 2: + val = 1; + break; + case 3: + case 4: + val = 2; + break; + case 5: + case 6: + val = 3; + break; + case 7: + case 8: + val = 4; + break; + default: + WARN_ONCE(1, "invalid nss: %d\n", + rx_status->nss); + val = 0; + } + he->data5 |= + le16_encode_bits(val, + IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS); + } + break; + case RATE_MCS_HE_TYPE_MU: { + u16 val; + + if (he_phy_data == HE_PHY_DATA_INVAL) + break; + + val = FIELD_GET(IWL_RX_HE_PHY_HE_LTF_NUM_MASK, + he_phy_data); + + he->data2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN); + he->data5 |= + cpu_to_le16(FIELD_PREP( + IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS, + val)); + } + break; + case RATE_MCS_HE_TYPE_TRIG: + /* not supported */ + break; + } +} + void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { @@ -869,12 +1275,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct ieee80211_sta *sta = NULL; struct sk_buff *skb; u8 crypt_len = 0, channel, energy_a, energy_b; - struct ieee80211_radiotap_he *he = NULL; - struct ieee80211_radiotap_he_mu *he_mu = NULL; - u32 he_type = 0xffffffff; - /* this is invalid e.g. because puncture type doesn't allow 0b11 */ -#define HE_PHY_DATA_INVAL ((u64)-1) - u64 he_phy_data = HE_PHY_DATA_INVAL; size_t desc_size; if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) @@ -918,49 +1318,24 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rx_status = IEEE80211_SKB_RXCB(skb); - if (rate_n_flags & RATE_MCS_HE_MSK) { - static const struct ieee80211_radiotap_he known = { - .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN), - .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | - IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN), - }; - static const struct ieee80211_radiotap_he_mu mu_known = { - .flags1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | - IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | - IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | - IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN), - .flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN), - }; - unsigned int radiotap_len = 0; - - he = skb_put_data(skb, &known, sizeof(known)); - radiotap_len += sizeof(known); - rx_status->flag |= RX_FLAG_RADIOTAP_HE; - - he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; - - if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) { - if (mvm->trans->cfg->device_family >= - IWL_DEVICE_FAMILY_22560) - he_phy_data = le64_to_cpu(desc->v3.he_phy_data); - else - he_phy_data = le64_to_cpu(desc->v1.he_phy_data); - - if (he_type == RATE_MCS_HE_TYPE_MU) { - he_mu = skb_put_data(skb, &mu_known, - sizeof(mu_known)); - radiotap_len += sizeof(mu_known); - rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; - } - } - - /* temporarily hide the radiotap data */ - __skb_pull(skb, radiotap_len); + /* This may be overridden by iwl_mvm_rx_he() to HE_RU */ + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + rx_status->bw = RATE_INFO_BW_40; + break; + case RATE_MCS_CHAN_WIDTH_80: + rx_status->bw = RATE_INFO_BW_80; + break; + case RATE_MCS_CHAN_WIDTH_160: + rx_status->bw = RATE_INFO_BW_160; + break; } + if (rate_n_flags & RATE_MCS_HE_MSK) + iwl_mvm_rx_he(mvm, skb, desc, rate_n_flags, phy_info, queue); + rx_status = IEEE80211_SKB_RXCB(skb); if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, phy_info, desc, @@ -995,53 +1370,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rx_status->mactime = tsf_on_air_rise; /* TSF as indicated by the firmware is at INA time */ rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; - } else if (he_type == RATE_MCS_HE_TYPE_SU) { - u64 he_phy_data; - - if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) - he_phy_data = le64_to_cpu(desc->v3.he_phy_data); - else - he_phy_data = le64_to_cpu(desc->v1.he_phy_data); - - he->data1 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN); - if (FIELD_GET(IWL_RX_HE_PHY_UPLINK, - he_phy_data)) - he->data3 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA3_UL_DL); - - if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { - rx_status->ampdu_reference = mvm->ampdu_ref; - mvm->ampdu_ref++; - - rx_status->flag |= RX_FLAG_AMPDU_DETAILS; - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; - if (FIELD_GET(IWL_RX_HE_PHY_DELIM_EOF, - he_phy_data)) - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; - } - } else if (he_mu && he_phy_data != HE_PHY_DATA_INVAL) { - he_mu->flags1 |= - le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_SIBG_SYM_OR_USER_NUM_MASK, - he_phy_data), - IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); - he_mu->flags1 |= - le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_SIGB_DCM, - he_phy_data), - IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); - he_mu->flags1 |= - le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_SIGB_MCS_MASK, - he_phy_data), - IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); - he_mu->flags2 |= - le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_SIGB_COMPRESSION, - he_phy_data), - IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); - he_mu->flags2 |= - le16_encode_bits(FIELD_GET(IWL_RX_HE_PHY_PREAMBLE_PUNC_TYPE_MASK, - he_phy_data), - IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); } + rx_status->device_timestamp = gp2_on_air_rise; rx_status->band = channel > 14 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; @@ -1066,15 +1396,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (toggle_bit != mvm->ampdu_toggle) { mvm->ampdu_ref++; mvm->ampdu_toggle = toggle_bit; - - if (he_phy_data != HE_PHY_DATA_INVAL && - he_type == RATE_MCS_HE_TYPE_MU) { - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; - if (FIELD_GET(IWL_RX_HE_PHY_DELIM_EOF, - he_phy_data)) - rx_status->flag |= - RX_FLAG_AMPDU_EOF_BIT; - } } } @@ -1183,84 +1504,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, } } - switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - break; - case RATE_MCS_CHAN_WIDTH_40: - rx_status->bw = RATE_INFO_BW_40; - break; - case RATE_MCS_CHAN_WIDTH_80: - rx_status->bw = RATE_INFO_BW_80; - break; - case RATE_MCS_CHAN_WIDTH_160: - rx_status->bw = RATE_INFO_BW_160; - break; - } - - if (he_type == RATE_MCS_HE_TYPE_EXT_SU && - rate_n_flags & RATE_MCS_HE_106T_MSK) { - rx_status->bw = RATE_INFO_BW_HE_RU; - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; - } - - if (rate_n_flags & RATE_MCS_HE_MSK && - phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD && - he_type == RATE_MCS_HE_TYPE_MU) { - /* - * Unfortunately, we have to leave the mac80211 data - * incorrect for the case that we receive an HE-MU - * transmission and *don't* have the he_mu pointer, - * i.e. we don't have the phy data (due to the bits - * being used for TSF). This shouldn't happen though - * as management frames where we need the TSF/timers - * are not be transmitted in HE-MU, I think. - */ - u8 ru = FIELD_GET(IWL_RX_HE_PHY_RU_ALLOC_MASK, he_phy_data); - u8 offs = 0; - - rx_status->bw = RATE_INFO_BW_HE_RU; - - switch (ru) { - case 0 ... 36: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; - offs = ru; - break; - case 37 ... 52: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; - offs = ru - 37; - break; - case 53 ... 60: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; - offs = ru - 53; - break; - case 61 ... 64: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; - offs = ru - 61; - break; - case 65 ... 66: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; - offs = ru - 65; - break; - case 67: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; - break; - case 68: - rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; - break; - } - he->data2 |= - le16_encode_bits(offs, - IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); - he->data2 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN); - if (he_phy_data & IWL_RX_HE_PHY_RU_ALLOC_SEC80) - he->data2 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC); - } else if (he) { - he->data1 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); - } - if (!(rate_n_flags & RATE_MCS_CCK_MSK) && rate_n_flags & RATE_MCS_SGI_MSK) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; @@ -1285,120 +1528,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; if (rate_n_flags & RATE_MCS_BF_MSK) rx_status->enc_flags |= RX_ENC_FLAG_BF; - } else if (he) { - u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> - RATE_MCS_STBC_POS; - rx_status->nss = - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->encoding = RX_ENC_HE; - rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; - if (rate_n_flags & RATE_MCS_BF_MSK) - rx_status->enc_flags |= RX_ENC_FLAG_BF; - - rx_status->he_dcm = - !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); - -#define CHECK_TYPE(F) \ - BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ - (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) - - CHECK_TYPE(SU); - CHECK_TYPE(EXT_SU); - CHECK_TYPE(MU); - CHECK_TYPE(TRIG); - - he->data1 |= cpu_to_le16(he_type >> RATE_MCS_HE_TYPE_POS); - - if (rate_n_flags & RATE_MCS_BF_POS) - he->data5 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA5_TXBF); - - switch ((rate_n_flags & RATE_MCS_HE_GI_LTF_MSK) >> - RATE_MCS_HE_GI_LTF_POS) { - case 0: - rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; - break; - case 1: - rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; - break; - case 2: - rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; - break; - case 3: - if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; - else - rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; - break; - } - - switch (he_type) { - case RATE_MCS_HE_TYPE_SU: { - u16 val; - - /* LTF syms correspond to streams */ - he->data2 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN); - switch (rx_status->nss) { - case 1: - val = 0; - break; - case 2: - val = 1; - break; - case 3: - case 4: - val = 2; - break; - case 5: - case 6: - val = 3; - break; - case 7: - case 8: - val = 4; - break; - default: - WARN_ONCE(1, "invalid nss: %d\n", - rx_status->nss); - val = 0; - } - he->data5 |= - le16_encode_bits(val, - IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS); - } - break; - case RATE_MCS_HE_TYPE_MU: { - u16 val; - u64 he_phy_data; - - if (mvm->trans->cfg->device_family >= - IWL_DEVICE_FAMILY_22560) - he_phy_data = le64_to_cpu(desc->v3.he_phy_data); - else - he_phy_data = le64_to_cpu(desc->v1.he_phy_data); - - if (he_phy_data == HE_PHY_DATA_INVAL) - break; - - val = FIELD_GET(IWL_RX_HE_PHY_HE_LTF_NUM_MASK, - he_phy_data); - - he->data2 |= - cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN); - he->data5 |= - cpu_to_le16(FIELD_PREP( - IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS, - val)); - } - break; - case RATE_MCS_HE_TYPE_EXT_SU: - case RATE_MCS_HE_TYPE_TRIG: - /* not supported yet */ - break; - } - } else { + } else if (!(rate_n_flags & RATE_MCS_HE_MSK)) { int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, rx_status->band); @@ -1409,7 +1539,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, goto out; } rx_status->rate_idx = rate; - } /* management stuff on default queue */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 11ecdf63b732..e9048a98e793 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -19,9 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -836,16 +833,25 @@ static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa; + bool low_latency; + + if (iwl_mvm_is_cdb_supported(mvm)) + low_latency = iwl_mvm_low_latency_band(mvm, NL80211_BAND_5GHZ); + else + low_latency = iwl_mvm_low_latency(mvm); /* We can only use EBS if: * 1. the feature is supported; * 2. the last EBS was successful; * 3. if only single scan, the single scan EBS API is supported; * 4. it's not a p2p find operation. + * 5. we are not in low latency mode, + * or if fragmented ebs is supported by the FW */ return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && mvm->last_ebs_successful && IWL_MVM_ENABLE_EBS && - vif->type != NL80211_IFTYPE_P2P_DEVICE); + vif->type != NL80211_IFTYPE_P2P_DEVICE && + (!low_latency || iwl_mvm_is_frag_ebs_supported(mvm))); } static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) @@ -1449,11 +1455,21 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT) cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); - if (iwl_mvm_scan_use_ebs(mvm, vif)) + if (iwl_mvm_scan_use_ebs(mvm, vif)) { channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + /* set fragmented ebs for fragmented scan on HB channels */ + if (iwl_mvm_is_frag_ebs_supported(mvm)) { + if (gen_flags & + IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED || + (!iwl_mvm_is_cdb_supported(mvm) && + gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED)) + channel_flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + } + } + chan_param->flags = channel_flags; chan_param->count = params->n_channels; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c index 539b06bf0803..d1d76bb9a750 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 18db1ed92d9b..8f929c774e70 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -72,6 +67,14 @@ #include "sta.h" #include "rs.h" +static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm); + +static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, + u32 sta_id, + struct ieee80211_key_conf *key, bool mcast, + u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, + u8 key_offset, bool mfp); + /* * New version of ADD_STA_sta command added new fields at the end of the * structure, so sending the size of the relevant API's structure is enough to @@ -2101,6 +2104,19 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, &cfg, timeout); + if (mvmvif->ap_wep_key) { + u8 key_offset = iwl_mvm_set_fw_key_idx(mvm); + + if (key_offset == STA_KEY_IDX_INVALID) + return -ENOSPC; + + ret = iwl_mvm_send_sta_key(mvm, mvmvif->mcast_sta.sta_id, + mvmvif->ap_wep_key, 1, 0, NULL, 0, + key_offset, 0); + if (ret) + return ret; + } + return 0; } @@ -3133,10 +3149,6 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_TKIP: - if (vif->type == NL80211_IFTYPE_AP) { - ret = -EINVAL; - break; - } addr = iwl_mvm_get_mac_addr(mvm, vif, sta); /* get phase 1 key from mac80211 */ ieee80211_get_key_rx_seq(keyconf, 0, &seq); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index 67f360c0d17e..e02f4eb20359 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -18,9 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h index cbbc16fd006a..ff82af11de8d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h index 3d2e8b6159bb..1dd3d01245ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h @@ -17,11 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c index 2d0b8a391308..01e0a999063b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h index 2ff560aa1a82..8138d0606c52 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h @@ -16,11 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 1232f63278eb..0b3e5c99d316 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -18,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index ff193dca2020..a6877b3f8037 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * 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 @@ -18,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -35,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -245,14 +242,18 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec, ssn); } else { - tx_cmd->tid_tspec = IWL_TID_NON_QOS; + if (ieee80211_is_data(fc)) + tx_cmd->tid_tspec = IWL_TID_NON_QOS; + else + tx_cmd->tid_tspec = IWL_MAX_TID_COUNT; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) tx_flags |= TX_CMD_FLG_SEQ_CTL; else tx_flags &= ~TX_CMD_FLG_SEQ_CTL; } - /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */ + /* Default to 0 (BE) when tid_spec is set to IWL_MAX_TID_COUNT */ if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT) ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; else @@ -620,6 +621,66 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm, } } +static void iwl_mvm_probe_resp_set_noa(struct iwl_mvm *mvm, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(info->control.vif); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + int base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt; + struct iwl_probe_resp_data *resp_data; + u8 *ie, *pos; + u8 match[] = { + (WLAN_OUI_WFA >> 16) & 0xff, + (WLAN_OUI_WFA >> 8) & 0xff, + WLAN_OUI_WFA & 0xff, + WLAN_OUI_TYPE_WFA_P2P, + }; + + rcu_read_lock(); + + resp_data = rcu_dereference(mvmvif->probe_resp_data); + if (!resp_data) + goto out; + + if (!resp_data->notif.noa_active) + goto out; + + ie = (u8 *)cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, + mgmt->u.probe_resp.variable, + skb->len - base_len, + match, 4, 2); + if (!ie) { + IWL_DEBUG_TX(mvm, "probe resp doesn't have P2P IE\n"); + goto out; + } + + if (skb_tailroom(skb) < resp_data->noa_len) { + if (pskb_expand_head(skb, 0, resp_data->noa_len, GFP_ATOMIC)) { + IWL_ERR(mvm, + "Failed to reallocate probe resp\n"); + goto out; + } + } + + pos = skb_put(skb, resp_data->noa_len); + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + /* Set length of IE body (not including ID and length itself) */ + *pos++ = resp_data->noa_len - 2; + *pos++ = (WLAN_OUI_WFA >> 16) & 0xff; + *pos++ = (WLAN_OUI_WFA >> 8) & 0xff; + *pos++ = WLAN_OUI_WFA & 0xff; + *pos++ = WLAN_OUI_TYPE_WFA_P2P; + + memcpy(pos, &resp_data->notif.noa_attr, + resp_data->noa_len - sizeof(struct ieee80211_vendor_ie)); + +out: + rcu_read_unlock(); +} + int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -628,6 +689,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) struct iwl_device_cmd *dev_cmd; u8 sta_id; int hdrlen = ieee80211_hdrlen(hdr->frame_control); + __le16 fc = hdr->frame_control; int queue; /* IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used @@ -668,7 +730,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) if (info.control.vif->type == NL80211_IFTYPE_P2P_DEVICE || info.control.vif->type == NL80211_IFTYPE_AP || info.control.vif->type == NL80211_IFTYPE_ADHOC) { - if (info.control.vif->type == NL80211_IFTYPE_P2P_DEVICE) + if (!ieee80211_is_data(hdr->frame_control)) sta_id = mvmvif->bcast_sta.sta_id; else sta_id = mvmvif->mcast_sta.sta_id; @@ -689,6 +751,9 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) } } + if (unlikely(ieee80211_is_probe_resp(fc))) + iwl_mvm_probe_resp_set_noa(mvm, skb); + IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, queue); dev_cmd = iwl_mvm_set_tx_params(mvm, skb, &info, hdrlen, NULL, sta_id); @@ -775,6 +840,36 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, return 0; } +static unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + unsigned int tid) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band; + u8 ac = tid_to_mac80211_ac[tid]; + unsigned int txf; + int lmac = IWL_LMAC_24G_INDEX; + + if (iwl_mvm_is_cdb_supported(mvm) && + band == NL80211_BAND_5GHZ) + lmac = IWL_LMAC_5G_INDEX; + + /* For HE redirect to trigger based fifos */ + if (sta->he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm))) + ac += 4; + + txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac); + + /* + * Don't send an AMSDU that will be longer than the TXF. + * Add a security margin of 256 for the TX command + headers. + * We also want to have the start of the next packet inside the + * fifo to be able to send bursts. + */ + return min_t(unsigned int, mvmsta->max_amsdu_len, + mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); +} + static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, @@ -787,7 +882,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, u16 snap_ip_tcp, pad; unsigned int dbg_max_amsdu_len; netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; - u8 tid, txf; + u8 tid; snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + tcp_hdrlen(skb); @@ -826,20 +921,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, !(mvmsta->amsdu_enabled & BIT(tid))) return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); - max_amsdu_len = mvmsta->max_amsdu_len; - - /* the Tx FIFO to which this A-MSDU will be routed */ - txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, tid_to_mac80211_ac[tid]); - - /* - * Don't send an AMSDU that will be longer than the TXF. - * Add a security margin of 256 for the TX command + headers. - * We also want to have the start of the next packet inside the - * fifo to be able to send bursts. - */ - max_amsdu_len = min_t(unsigned int, max_amsdu_len, - mvm->fwrt.smem_cfg.lmac[0].txfifo_size[txf] - - 256); + max_amsdu_len = iwl_mvm_max_amsdu_size(mvm, sta, tid); if (unlikely(dbg_max_amsdu_len)) max_amsdu_len = min_t(unsigned int, max_amsdu_len, @@ -1010,6 +1092,9 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) return -1; + if (unlikely(ieee80211_is_probe_resp(fc))) + iwl_mvm_probe_resp_set_noa(mvm, skb); + dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen, sta, mvmsta->sta_id); if (!dev_cmd) @@ -1049,6 +1134,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, /* update the tx_cmd hdr as it was already copied */ tx_cmd->hdr->seq_ctrl = hdr->seq_ctrl; } + } else if (ieee80211_is_data(fc) && !ieee80211_is_data_qos(fc)) { + tid = IWL_TID_NON_QOS; } txq_id = mvmsta->tid_data[tid].txq_id; @@ -1405,6 +1492,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, while (!skb_queue_empty(&skbs)) { struct sk_buff *skb = __skb_dequeue(&skbs); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; bool flushed = false; skb_freed++; @@ -1434,6 +1522,14 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, break; } + /* + * If we are freeing multiple frames, mark all the frames + * but the first one as acked, since they were acknowledged + * before + * */ + if (skb_freed > 1) + info->flags |= IEEE80211_TX_STAT_ACK; + iwl_mvm_tx_status_check_trigger(mvm, status); info->status.rates[0].count = tx_resp->failure_frame + 1; @@ -1449,11 +1545,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; info->flags &= ~IEEE80211_TX_CTL_AMPDU; - /* W/A FW bug: seq_ctl is wrong when the status isn't success */ - if (status != TX_STATUS_SUCCESS) { - struct ieee80211_hdr *hdr = (void *)skb->data; + /* W/A FW bug: seq_ctl is wrong upon failure / BAR frame */ + if (ieee80211_is_back_req(hdr->frame_control)) + seq_ctl = 0; + else if (status != TX_STATUS_SUCCESS) seq_ctl = le16_to_cpu(hdr->seq_ctrl); - } if (unlikely(!seq_ctl)) { struct ieee80211_hdr *hdr = (void *)skb->data; @@ -1525,7 +1621,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, iwl_mvm_tx_airtime(mvm, mvmsta, le16_to_cpu(tx_resp->wireless_media_time)); - if (tid != IWL_TID_NON_QOS && tid != IWL_MGMT_TID) { + if (sta->wme && tid != IWL_MGMT_TID) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; bool send_eosp_ndp = false; @@ -1645,20 +1741,24 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, u16 sequence = le16_to_cpu(pkt->hdr.sequence); struct iwl_mvm_sta *mvmsta; int queue = SEQ_TO_QUEUE(sequence); + struct ieee80211_sta *sta; if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE && (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE))) return; - if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) - return; - iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt); rcu_read_lock(); mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (WARN_ON_ONCE(!sta || !sta->wme)) { + rcu_read_unlock(); + return; + } + if (!WARN_ON_ONCE(!mvmsta)) { mvmsta->tid_data[tid].rate_n_flags = le32_to_cpu(tx_resp->initial_rate); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index b002a7afb5f5..dcacc4d11abc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -551,7 +546,6 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base) IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); - trace_iwlwifi_dev_ucode_error(trans->dev, &table, table.hw_ver, table.brd_ver); IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); IWL_ERR(mvm, "0x%08X | trm_hw_status0\n", table.trm_hw_status0); @@ -725,19 +719,15 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue, int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, u8 sta_id, u8 tid, unsigned int timeout) { - struct iwl_tx_queue_cfg_cmd cmd = { - .flags = cpu_to_le16(TX_QUEUE_CFG_ENABLE_QUEUE), - .sta_id = sta_id, - .tid = tid, - }; int queue, size = IWL_DEFAULT_QUEUE_SIZE; - if (cmd.tid == IWL_MAX_TID_COUNT) { - cmd.tid = IWL_MGMT_TID; + if (tid == IWL_MAX_TID_COUNT) { + tid = IWL_MGMT_TID; size = IWL_MGMT_QUEUE_SIZE; } - queue = iwl_trans_txq_alloc(mvm->trans, (void *)&cmd, - SCD_QUEUE_CFG, size, timeout); + queue = iwl_trans_txq_alloc(mvm->trans, + cpu_to_le16(TX_QUEUE_CFG_ENABLE_QUEUE), + sta_id, tid, SCD_QUEUE_CFG, size, timeout); if (queue < 0) { IWL_DEBUG_TX_QUEUES(mvm, @@ -900,20 +890,19 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, /** * iwl_mvm_send_lq_cmd() - Send link quality command - * @init: This command is sent as part of station initialization right - * after station has been added. + * @sync: This command can be sent synchronously. * * The link quality command is sent as the last step of station creation. * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress. */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool sync) { struct iwl_host_cmd cmd = { .id = LQ_CMD, .len = { sizeof(struct iwl_lq_cmd), }, - .flags = init ? 0 : CMD_ASYNC, + .flags = sync ? 0 : CMD_ASYNC, .data = { lq, }, }; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index 2146fda8da2f..05ed4fb88e0c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -96,9 +96,9 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, /* Configure debug, for integration */ iwl_pcie_alloc_fw_monitor(trans, 0); prph_sc_ctrl->hwm_cfg.hwm_base_addr = - cpu_to_le64(trans_pcie->fw_mon_phys); + cpu_to_le64(trans->fw_mon[0].physical); prph_sc_ctrl->hwm_cfg.hwm_size = - cpu_to_le32(trans_pcie->fw_mon_size); + cpu_to_le32(trans->fw_mon[0].size); /* allocate ucode sections in dram and set addresses */ ret = iwl_pcie_init_fw_sec(trans, fw, &prph_scratch->dram); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index b2cd7ef5fc3a..6f45a0303ddd 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -162,7 +162,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_context_info *ctxt_info; struct iwl_context_info_rbd_cfg *rx_cfg; - u32 control_flags = 0; + u32 control_flags = 0, rb_size; int ret; ctxt_info = dma_alloc_coherent(trans->dev, sizeof(*ctxt_info), @@ -177,11 +177,29 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, /* size is in DWs */ ctxt_info->version.size = cpu_to_le16(sizeof(*ctxt_info) / 4); + switch (trans_pcie->rx_buf_size) { + case IWL_AMSDU_2K: + rb_size = IWL_CTXT_INFO_RB_SIZE_2K; + break; + case IWL_AMSDU_4K: + rb_size = IWL_CTXT_INFO_RB_SIZE_4K; + break; + case IWL_AMSDU_8K: + rb_size = IWL_CTXT_INFO_RB_SIZE_8K; + break; + case IWL_AMSDU_12K: + rb_size = IWL_CTXT_INFO_RB_SIZE_12K; + break; + default: + WARN_ON(1); + rb_size = IWL_CTXT_INFO_RB_SIZE_4K; + } + BUILD_BUG_ON(RX_QUEUE_CB_SIZE(MQ_RX_TABLE_SIZE) > 0xF); - control_flags = IWL_CTXT_INFO_RB_SIZE_4K | - IWL_CTXT_INFO_TFD_FORMAT_LONG | - RX_QUEUE_CB_SIZE(MQ_RX_TABLE_SIZE) << - IWL_CTXT_INFO_RB_CB_SIZE_POS; + control_flags = IWL_CTXT_INFO_TFD_FORMAT_LONG | + (RX_QUEUE_CB_SIZE(MQ_RX_TABLE_SIZE) << + IWL_CTXT_INFO_RB_CB_SIZE_POS) | + (rb_size << IWL_CTXT_INFO_RB_SIZE_POS); ctxt_info->control.control_flags = cpu_to_le32(control_flags); /* initialize RX default queue */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index b150da4c6721..9e015212c2c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -646,34 +641,33 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_shared_clk)}, {IWL_PCI_DEVICE(0x31DC, 0x4234, iwl9560_2ac_cfg_shared_clk)}, {IWL_PCI_DEVICE(0x31DC, 0x42A4, iwl9462_2ac_cfg_shared_clk)}, - {IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0034, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0038, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x003C, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0060, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0064, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x00A0, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x00A4, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0230, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0234, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0238, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x023C, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0260, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x0264, iwl9461_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x02A0, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x02A4, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x1010, iwl9260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x34F0, 0x1030, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x1210, iwl9260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x34F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x1552, iwl9560_killer_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x2030, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x2034, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x4030, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x4034, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x40A4, iwl9462_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x4234, iwl9560_2ac_cfg_soc)}, - {IWL_PCI_DEVICE(0x34F0, 0x42A4, iwl9462_2ac_cfg_soc)}, + + {IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0034, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0038, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x003C, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0060, iwl9461_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0064, iwl9461_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x00A0, iwl9462_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x00A4, iwl9462_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0230, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0234, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0238, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x023C, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0260, iwl9461_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x0264, iwl9461_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x02A0, iwl9462_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x02A4, iwl9462_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x1551, killer1550s_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x1552, killer1550i_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x2030, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x2034, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x4030, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x4034, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x40A4, iwl9462_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x4234, iwl9560_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x34F0, 0x42A4, iwl9462_2ac_cfg_qu_b0_jf_b0)}, + {IWL_PCI_DEVICE(0x3DF0, 0x0030, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x3DF0, 0x0034, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x3DF0, 0x0038, iwl9560_2ac_cfg_soc)}, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index b63d44b7cd7c..f9c4c64dee66 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -17,9 +17,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -102,66 +99,6 @@ struct isr_statistics { u32 unhandled; }; -#define IWL_CD_STTS_OPTIMIZED_POS 0 -#define IWL_CD_STTS_OPTIMIZED_MSK 0x01 -#define IWL_CD_STTS_TRANSFER_STATUS_POS 1 -#define IWL_CD_STTS_TRANSFER_STATUS_MSK 0x0E -#define IWL_CD_STTS_WIFI_STATUS_POS 4 -#define IWL_CD_STTS_WIFI_STATUS_MSK 0xF0 - -/** - * enum iwl_completion_desc_transfer_status - transfer status (bits 1-3) - * @IWL_CD_STTS_END_TRANSFER: successful transfer complete. - * In sniffer mode, when split is used, set in last CD completion. (RX) - * @IWL_CD_STTS_OVERFLOW: In sniffer mode, when using split - used for - * all CD completion. (RX) - * @IWL_CD_STTS_ABORTED: CR abort / close flow. (RX) - */ -enum iwl_completion_desc_transfer_status { - IWL_CD_STTS_UNUSED, - IWL_CD_STTS_UNUSED_2, - IWL_CD_STTS_END_TRANSFER, - IWL_CD_STTS_OVERFLOW, - IWL_CD_STTS_ABORTED, - IWL_CD_STTS_ERROR, -}; - -/** - * enum iwl_completion_desc_wifi_status - wifi status (bits 4-7) - * @IWL_CD_STTS_VALID: the packet is valid (RX) - * @IWL_CD_STTS_FCS_ERR: frame check sequence error (RX) - * @IWL_CD_STTS_SEC_KEY_ERR: error handling the security key of rx (RX) - * @IWL_CD_STTS_DECRYPTION_ERR: error decrypting the frame (RX) - * @IWL_CD_STTS_DUP: duplicate packet (RX) - * @IWL_CD_STTS_ICV_MIC_ERR: MIC error (RX) - * @IWL_CD_STTS_INTERNAL_SNAP_ERR: problems removing the snap (RX) - * @IWL_CD_STTS_SEC_PORT_FAIL: security port fail (RX) - * @IWL_CD_STTS_BA_OLD_SN: block ack received old SN (RX) - * @IWL_CD_STTS_QOS_NULL: QoS null packet (RX) - * @IWL_CD_STTS_MAC_HDR_ERR: MAC header conversion error (RX) - * @IWL_CD_STTS_MAX_RETRANS: reached max number of retransmissions (TX) - * @IWL_CD_STTS_EX_LIFETIME: exceeded lifetime (TX) - * @IWL_CD_STTS_NOT_USED: completed but not used (RX) - * @IWL_CD_STTS_REPLAY_ERR: pn check failed, replay error (RX) - */ -enum iwl_completion_desc_wifi_status { - IWL_CD_STTS_VALID, - IWL_CD_STTS_FCS_ERR, - IWL_CD_STTS_SEC_KEY_ERR, - IWL_CD_STTS_DECRYPTION_ERR, - IWL_CD_STTS_DUP, - IWL_CD_STTS_ICV_MIC_ERR, - IWL_CD_STTS_INTERNAL_SNAP_ERR, - IWL_CD_STTS_SEC_PORT_FAIL, - IWL_CD_STTS_BA_OLD_SN, - IWL_CD_STTS_QOS_NULL, - IWL_CD_STTS_MAC_HDR_ERR, - IWL_CD_STTS_MAX_RETRANS, - IWL_CD_STTS_EX_LIFETIME, - IWL_CD_STTS_NOT_USED, - IWL_CD_STTS_REPLAY_ERR, -}; - #define IWL_RX_TD_TYPE_MSK 0xff000000 #define IWL_RX_TD_SIZE_MSK 0x00ffffff #define IWL_RX_TD_SIZE_2K BIT(11) @@ -464,18 +401,6 @@ enum iwl_image_response_code { }; /** - * struct iwl_dram_data - * @physical: page phy pointer - * @block: pointer to the allocated block/page - * @size: size of the block/page - */ -struct iwl_dram_data { - dma_addr_t physical; - void *block; - int size; -}; - -/** * struct iwl_self_init_dram - dram data used by self init process * @fw: lmac and umac dram data * @fw_cnt: total number of items in array @@ -516,6 +441,7 @@ struct iwl_self_init_dram { * @ucode_write_complete: indicates that the ucode has been copied. * @ucode_write_waitq: wait queue for uCode load * @cmd_queue - command queue number + * @def_rx_queue - default rx queue number * @rx_buf_size: Rx buffer size * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue @@ -525,9 +451,6 @@ struct iwl_self_init_dram { * @reg_lock: protect hw register access * @mutex: to protect stop_device / start_fw / start_hw * @cmd_in_flight: true when we have a host command in flight - * @fw_mon_phys: physical address of the buffer for the firmware monitor - * @fw_mon_page: points to the first page of the buffer for the firmware monitor - * @fw_mon_size: size of the buffer for the firmware monitor * @msix_entries: array of MSI-X entries * @msix_enabled: true if managed to enable MSI-X * @shared_vec_mask: the type of causes the shared vector handles @@ -539,7 +462,6 @@ struct iwl_self_init_dram { * @fh_mask: current unmasked fh causes * @hw_mask: current unmasked hw causes * @in_rescan: true if we have triggered a device rescan - * @scheduled_for_removal: true if we have scheduled a device removal */ struct iwl_trans_pcie { struct iwl_rxq *rxq; @@ -596,6 +518,7 @@ struct iwl_trans_pcie { u8 page_offs, dev_cmd_offs; u8 cmd_queue; + u8 def_rx_queue; u8 cmd_fifo; unsigned int cmd_q_wdg_timeout; u8 n_no_reclaim_cmds; @@ -615,10 +538,6 @@ struct iwl_trans_pcie { bool cmd_hold_nic_awake; bool ref_cmd_in_flight; - dma_addr_t fw_mon_phys; - struct page *fw_mon_page; - u32 fw_mon_size; - struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES]; bool msix_enabled; u8 shared_vec_mask; @@ -631,7 +550,6 @@ struct iwl_trans_pcie { cpumask_t affinity_mask[IWL_MAX_RX_HW_QUEUES]; u16 tx_cmd_queue_size; bool in_rescan; - bool scheduled_for_removal; }; static inline struct iwl_trans_pcie * @@ -673,6 +591,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); /***************************************************** * RX ******************************************************/ +int _iwl_pcie_rx_init(struct iwl_trans *trans); int iwl_pcie_rx_init(struct iwl_trans *trans); int iwl_pcie_gen2_rx_init(struct iwl_trans *trans); irqreturn_t iwl_pcie_msix_isr(int irq, void *data); @@ -686,6 +605,7 @@ void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq); int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget); void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority, struct iwl_rxq *rxq); +int iwl_pcie_rx_alloc(struct iwl_trans *trans); /***************************************************** * ICT - interrupt handling @@ -700,7 +620,8 @@ void iwl_pcie_disable_ict(struct iwl_trans *trans); * TX / HCMD ******************************************************/ int iwl_pcie_tx_init(struct iwl_trans *trans); -int iwl_pcie_gen2_tx_init(struct iwl_trans *trans); +int iwl_pcie_gen2_tx_init(struct iwl_trans *trans, int txq_id, + int queue_size); void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); int iwl_pcie_tx_stop(struct iwl_trans *trans); void iwl_pcie_tx_free(struct iwl_trans *trans); @@ -717,11 +638,17 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx); +void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_txq *txq); void iwl_pcie_hcmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb); void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); +void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans_pcie *trans_pcie, + struct iwl_txq *txq, u16 byte_cnt, + int num_tbs); static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_trans *trans, void *_tfd, u8 idx) @@ -1039,6 +966,7 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, } void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); +void iwl_trans_pcie_dump_regs(struct iwl_trans *trans); #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans); @@ -1057,6 +985,7 @@ void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable); void iwl_pcie_rx_allocator_work(struct work_struct *data); /* common functions that are used by gen2 transport */ +int iwl_pcie_gen2_apm_init(struct iwl_trans *trans); void iwl_pcie_apm_config(struct iwl_trans *trans); int iwl_pcie_prepare_card_hw(struct iwl_trans *trans); void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); @@ -1088,8 +1017,16 @@ void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill); void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr); +void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans, + struct iwl_txq *txq); +int iwl_trans_pcie_dyn_txq_alloc_dma(struct iwl_trans *trans, + struct iwl_txq **intxq, int size, + unsigned int timeout); +int iwl_trans_pcie_txq_alloc_response(struct iwl_trans *trans, + struct iwl_txq *txq, + struct iwl_host_cmd *hcmd); int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue_cfg_cmd *cmd, + __le16 flags, u8 sta_id, u8 tid, int cmd_id, int size, unsigned int timeout); void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index d017aa2a0a8b..d519e7ebdbe8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -17,9 +17,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -776,7 +773,7 @@ err: return -ENOMEM; } -static int iwl_pcie_rx_alloc(struct iwl_trans *trans) +int iwl_pcie_rx_alloc(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rb_allocator *rba = &trans_pcie->rba; @@ -1002,7 +999,7 @@ int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) return 0; } -static int _iwl_pcie_rx_init(struct iwl_trans *trans) +int _iwl_pcie_rx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *def_rxq; @@ -1107,6 +1104,9 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) int iwl_pcie_gen2_rx_init(struct iwl_trans *trans) { + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); + /* * We don't configure the RFH. * Restock will be done at alive, after firmware configured the RFH. @@ -1187,7 +1187,8 @@ static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans, static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, struct iwl_rxq *rxq, struct iwl_rx_mem_buffer *rxb, - bool emergency) + bool emergency, + int i) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; @@ -1213,6 +1214,9 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, .truesize = max_len, }; + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) + rxcb.status = rxq->cd[i].status; + pkt = rxb_addr(&rxcb); if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) { @@ -1267,7 +1271,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, index = SEQ_TO_INDEX(sequence); cmd_index = iwl_pcie_get_cmd_index(txq, index); - if (rxq->id == 0) + if (rxq->id == trans_pcie->def_rx_queue) iwl_op_mode_rx(trans->op_mode, &rxq->napi, &rxcb); else @@ -1406,7 +1410,7 @@ restart: goto out; IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i); - iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency); + iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency, i); i = (i + 1) & (rxq->queue_size - 1); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 2bc67219ed3e..77f3610e5ca9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -55,13 +55,14 @@ #include "iwl-context-info.h" #include "iwl-context-info-gen3.h" #include "internal.h" +#include "fw/dbg.h" /* * Start up NIC's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ -static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) +int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) { int ret = 0; @@ -164,9 +165,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) trans_pcie->is_down = true; /* Stop dbgc before stopping device */ - iwl_write_prph(trans, DBGC_IN_SAMPLE, 0); - udelay(100); - iwl_write_prph(trans, DBGC_OUT_CTRL, 0); + _iwl_fw_dbg_stop_recording(trans, NULL); /* tell the device to stop sending interrupts */ iwl_disable_interrupts(trans); @@ -265,7 +264,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) return -ENOMEM; /* Allocate or reset and init all Tx and Command queues */ - if (iwl_pcie_gen2_tx_init(trans)) + if (iwl_pcie_gen2_tx_init(trans, trans_pcie->cmd_queue, TFD_CMD_SLOTS)) return -ENOMEM; /* enable shadow regs in HW */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 7d319b6863fe..bc6682a11fa4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -19,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -92,7 +87,7 @@ #define IWL_FW_MEM_EXTENDED_START 0x40000 #define IWL_FW_MEM_EXTENDED_END 0x57FFF -static void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) +void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) { #define PCI_DUMP_SIZE 64 #define PREFIX_LEN 32 @@ -190,72 +185,42 @@ static void iwl_trans_pcie_sw_reset(struct iwl_trans *trans) static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (!trans_pcie->fw_mon_page) - return; + int i; - dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys, - trans_pcie->fw_mon_size, DMA_FROM_DEVICE); - __free_pages(trans_pcie->fw_mon_page, - get_order(trans_pcie->fw_mon_size)); - trans_pcie->fw_mon_page = NULL; - trans_pcie->fw_mon_phys = 0; - trans_pcie->fw_mon_size = 0; + for (i = 0; i < trans->num_blocks; i++) { + dma_free_coherent(trans->dev, trans->fw_mon[i].size, + trans->fw_mon[i].block, + trans->fw_mon[i].physical); + trans->fw_mon[i].block = NULL; + trans->fw_mon[i].physical = 0; + trans->fw_mon[i].size = 0; + trans->num_blocks--; + } } -void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) +static void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans, + u8 max_power, u8 min_power) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct page *page = NULL; - dma_addr_t phys; + void *cpu_addr = NULL; + dma_addr_t phys = 0; u32 size = 0; u8 power; - if (!max_power) { - /* default max_power is maximum */ - max_power = 26; - } else { - max_power += 11; - } - - if (WARN(max_power > 26, - "External buffer size for monitor is too big %d, check the FW TLV\n", - max_power)) - return; - - if (trans_pcie->fw_mon_page) { - dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys, - trans_pcie->fw_mon_size, - DMA_FROM_DEVICE); - return; - } - - phys = 0; - for (power = max_power; power >= 11; power--) { - int order; - + for (power = max_power; power >= min_power; power--) { size = BIT(power); - order = get_order(size); - page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO, - order); - if (!page) + cpu_addr = dma_alloc_coherent(trans->dev, size, &phys, + GFP_KERNEL | __GFP_NOWARN | + __GFP_ZERO | __GFP_COMP); + if (!cpu_addr) continue; - phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order, - DMA_FROM_DEVICE); - if (dma_mapping_error(trans->dev, phys)) { - __free_pages(page, order); - page = NULL; - continue; - } IWL_INFO(trans, - "Allocated 0x%08x bytes (order %d) for firmware monitor.\n", - size, order); + "Allocated 0x%08x bytes for firmware monitor.\n", + size); break; } - if (WARN_ON_ONCE(!page)) + if (WARN_ON_ONCE(!cpu_addr)) return; if (power != max_power) @@ -264,9 +229,34 @@ void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) (unsigned long)BIT(power - 10), (unsigned long)BIT(max_power - 10)); - trans_pcie->fw_mon_page = page; - trans_pcie->fw_mon_phys = phys; - trans_pcie->fw_mon_size = size; + trans->fw_mon[trans->num_blocks].block = cpu_addr; + trans->fw_mon[trans->num_blocks].physical = phys; + trans->fw_mon[trans->num_blocks].size = size; + trans->num_blocks++; +} + +void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) +{ + if (!max_power) { + /* default max_power is maximum */ + max_power = 26; + } else { + max_power += 11; + } + + if (WARN(max_power > 26, + "External buffer size for monitor is too big %d, check the FW TLV\n", + max_power)) + return; + + /* + * This function allocats the default fw monitor. + * The optional additional ones will be allocated in runtime + */ + if (trans->num_blocks) + return; + + iwl_pcie_alloc_fw_monitor_block(trans, max_power, 11); } static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) @@ -930,7 +920,6 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, void iwl_pcie_apply_destination(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv; int i; @@ -981,18 +970,18 @@ void iwl_pcie_apply_destination(struct iwl_trans *trans) } monitor: - if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { + if (dest->monitor_mode == EXTERNAL_MODE && trans->fw_mon[0].size) { iwl_write_prph(trans, le32_to_cpu(dest->base_reg), - trans_pcie->fw_mon_phys >> dest->base_shift); + trans->fw_mon[0].physical >> dest->base_shift); if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size - 256) >> + (trans->fw_mon[0].physical + + trans->fw_mon[0].size - 256) >> dest->end_shift); else iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size) >> + (trans->fw_mon[0].physical + + trans->fw_mon[0].size) >> dest->end_shift); } } @@ -1000,7 +989,6 @@ monitor: static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; int first_ucode_section; @@ -1030,12 +1018,12 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { iwl_pcie_alloc_fw_monitor(trans, 0); - if (trans_pcie->fw_mon_size) { + if (trans->fw_mon[0].size) { iwl_write_prph(trans, MON_BUFF_BASE_ADDR, - trans_pcie->fw_mon_phys >> 4); + trans->fw_mon[0].physical >> 4); iwl_write_prph(trans, MON_BUFF_END_ADDR, - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size) >> 4); + (trans->fw_mon[0].physical + + trans->fw_mon[0].size) >> 4); } } else if (trans->dbg_dest_tlv) { iwl_pcie_apply_destination(trans); @@ -1262,13 +1250,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) trans_pcie->is_down = true; /* Stop dbgc before stopping device */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); - } else { - iwl_write_prph(trans, DBGC_IN_SAMPLE, 0); - udelay(100); - iwl_write_prph(trans, DBGC_OUT_CTRL, 0); - } + _iwl_fw_dbg_stop_recording(trans, NULL); /* tell the device to stop sending interrupts */ iwl_disable_interrupts(trans); @@ -1830,18 +1812,30 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); } +static u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans) +{ + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) + return 0x00FFFFFF; + else + return 0x000FFFFF; +} + static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg) { + u32 mask = iwl_trans_pcie_prph_msk(trans); + iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, - ((reg & 0x000FFFFF) | (3 << 24))); + ((reg & mask) | (3 << 24))); return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT); } static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val) { + u32 mask = iwl_trans_pcie_prph_msk(trans); + iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, - ((addr & 0x000FFFFF) | (3 << 24))); + ((addr & mask) | (3 << 24))); iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); } @@ -2013,7 +2007,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) { struct iwl_trans_pcie_removal *removal; - if (trans_pcie->scheduled_for_removal) + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) goto err; IWL_ERR(trans, "Device gone - scheduling removal!\n"); @@ -2039,7 +2033,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, * we don't need to clear this flag, because * the trans will be freed and reallocated. */ - trans_pcie->scheduled_for_removal = true; + set_bit(STATUS_TRANS_DEAD, &trans->status); removal->pdev = to_pci_dev(trans->dev); INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); @@ -2266,6 +2260,10 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) unsigned long now = jiffies; u8 wr_ptr; + /* Make sure the NIC is still alive in the bus */ + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return -ENODEV; + if (!test_bit(txq_idx, trans_pcie->queue_used)) return -EINVAL; @@ -2861,10 +2859,9 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data, u32 monitor_len) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 len = 0; - if ((trans_pcie->fw_mon_page && + if ((trans->num_blocks && trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) || trans->dbg_dest_tlv) { struct iwl_fw_error_dump_fw_mon *fw_mon_data; @@ -2892,22 +2889,12 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, cpu_to_le32(iwl_read_prph(trans, base)); len += sizeof(**data) + sizeof(*fw_mon_data); - if (trans_pcie->fw_mon_page) { - /* - * The firmware is now asserted, it won't write anything - * to the buffer. CPU can take ownership to fetch the - * data. The buffer will be handed back to the device - * before the firmware will be restarted. - */ - dma_sync_single_for_cpu(trans->dev, - trans_pcie->fw_mon_phys, - trans_pcie->fw_mon_size, - DMA_FROM_DEVICE); + if (trans->num_blocks) { memcpy(fw_mon_data->data, - page_address(trans_pcie->fw_mon_page), - trans_pcie->fw_mon_size); + trans->fw_mon[0].block, + trans->fw_mon[0].size); - monitor_len = trans_pcie->fw_mon_size; + monitor_len = trans->fw_mon[0].size; } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) { /* * Update pointers to reflect actual values after @@ -2943,36 +2930,15 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, return len; } -static struct iwl_trans_dump_data -*iwl_trans_pcie_dump_data(struct iwl_trans *trans, - const struct iwl_fw_dbg_trigger_tlv *trigger) +static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_fw_error_dump_data *data; - struct iwl_txq *cmdq = trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_fw_error_dump_txcmd *txcmd; - struct iwl_trans_dump_data *dump_data; - u32 len, num_rbs = 0; - u32 monitor_len; - int i, ptr; - bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && - !trans->cfg->mq_rx_supported && - trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); - - /* transport dump header */ - len = sizeof(*dump_data); - - /* host commands */ - len += sizeof(*data) + - cmdq->n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); - - /* FW monitor */ - if (trans_pcie->fw_mon_page) { - len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + - trans_pcie->fw_mon_size; - monitor_len = trans_pcie->fw_mon_size; + if (trans->num_blocks) { + *len += sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_fw_mon) + + trans->fw_mon[0].size; + return trans->fw_mon[0].size; } else if (trans->dbg_dest_tlv) { - u32 base, end, cfg_reg; + u32 base, end, cfg_reg, monitor_len; if (trans->dbg_dest_tlv->version == 1) { cfg_reg = le32_to_cpu(trans->dbg_dest_tlv->base_reg); @@ -3002,11 +2968,39 @@ static struct iwl_trans_dump_data end += (1 << trans->dbg_dest_tlv->end_shift); monitor_len = end - base; } - len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + - monitor_len; - } else { - monitor_len = 0; + *len += sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_fw_mon) + + monitor_len; + return monitor_len; } + return 0; +} + +static struct iwl_trans_dump_data +*iwl_trans_pcie_dump_data(struct iwl_trans *trans, + const struct iwl_fw_dbg_trigger_tlv *trigger) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_fw_error_dump_data *data; + struct iwl_txq *cmdq = trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_fw_error_dump_txcmd *txcmd; + struct iwl_trans_dump_data *dump_data; + u32 len, num_rbs = 0; + u32 monitor_len; + int i, ptr; + bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && + !trans->cfg->mq_rx_supported && + trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); + + /* transport dump header */ + len = sizeof(*dump_data); + + /* host commands */ + len += sizeof(*data) + + cmdq->n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); + + /* FW monitor */ + monitor_len = iwl_trans_get_fw_monitor_len(trans, &len); if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) { if (!(trans->dbg_dump_mask & @@ -3175,7 +3169,6 @@ static void iwl_trans_pcie_resume(struct iwl_trans *trans) .ref = iwl_trans_pcie_ref, \ .unref = iwl_trans_pcie_unref, \ .dump_data = iwl_trans_pcie_dump_data, \ - .dump_regs = iwl_trans_pcie_dump_regs, \ .d3_suspend = iwl_trans_pcie_d3_suspend, \ .d3_resume = iwl_trans_pcie_d3_resume @@ -3277,6 +3270,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, PCIE_LINK_STATE_CLKPM); } + trans_pcie->def_rx_queue = 0; + if (cfg->use_tfh) { addr_size = 64; trans_pcie->max_tbs = IWL_TFH_NUM_TBS; @@ -3327,6 +3322,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, iwl_disable_interrupts(trans); trans->hw_rev = iwl_read32(trans, CSR_HW_REV); + if (trans->hw_rev == 0xffffffff) { + dev_err(&pdev->dev, "HW_REV=0xFFFFFFFF, PCI issues?\n"); + ret = -EIO; + goto out_no_pci; + } + /* * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have * changed, and now the revision step also includes bit 0-1 (no more diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index b99f33ff9123..ba9d37bed4c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -87,9 +87,9 @@ void iwl_pcie_gen2_tx_stop(struct iwl_trans *trans) /* * iwl_pcie_txq_update_byte_tbl - Set up entry in Tx byte-count array */ -static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans_pcie *trans_pcie, - struct iwl_txq *txq, u16 byte_cnt, - int num_tbs) +void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans_pcie *trans_pcie, + struct iwl_txq *txq, u16 byte_cnt, + int num_tbs) { struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr; struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); @@ -127,8 +127,8 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans_pcie *trans_pcie, /* * iwl_pcie_gen2_txq_inc_wr_ptr - Send new write index to hardware */ -static void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, - struct iwl_txq *txq) +void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_txq *txq) { lockdep_assert_held(&txq->lock); @@ -416,6 +416,35 @@ out_err: return NULL; } +static int iwl_pcie_gen2_tx_add_frags(struct iwl_trans *trans, + struct sk_buff *skb, + struct iwl_tfh_tfd *tfd, + struct iwl_cmd_meta *out_meta) +{ + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t tb_phys; + int tb_idx; + + if (!skb_frag_size(frag)) + continue; + + tb_phys = skb_frag_dma_map(trans->dev, frag, 0, + skb_frag_size(frag), DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) + return -ENOMEM; + tb_idx = iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, + skb_frag_size(frag)); + + out_meta->tbs |= BIT(tb_idx); + } + + return 0; +} + static struct iwl_tfh_tfd *iwl_pcie_gen2_build_tx(struct iwl_trans *trans, struct iwl_txq *txq, @@ -428,7 +457,7 @@ iwl_tfh_tfd *iwl_pcie_gen2_build_tx(struct iwl_trans *trans, int idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans, txq, idx); dma_addr_t tb_phys; - int i, len, tb1_len, tb2_len; + int len, tb1_len, tb2_len; void *tb1_addr; tb_phys = iwl_pcie_get_first_tb_dma(txq, idx); @@ -467,24 +496,8 @@ iwl_tfh_tfd *iwl_pcie_gen2_build_tx(struct iwl_trans *trans, iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb2_len); } - /* set up the remaining entries to point to the data */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - int tb_idx; - - if (!skb_frag_size(frag)) - continue; - - tb_phys = skb_frag_dma_map(trans->dev, frag, 0, - skb_frag_size(frag), DMA_TO_DEVICE); - - if (unlikely(dma_mapping_error(trans->dev, tb_phys))) - goto out_err; - tb_idx = iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, - skb_frag_size(frag)); - - out_meta->tbs |= BIT(tb_idx); - } + if (iwl_pcie_gen2_tx_add_frags(trans, skb, tfd, out_meta)) + goto out_err; trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd), &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, hdr_len); @@ -526,7 +539,12 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, hdr_len = ieee80211_hdrlen(hdr->frame_control); - if (amsdu) + /* + * Only build A-MSDUs here if doing so by GSO, otherwise it may be + * an A-MSDU for other reasons, e.g. NAN or an A-MSDU having been + * built in the higher layers already. + */ + if (amsdu && skb_shinfo(skb)->gso_size) return iwl_pcie_gen2_build_tx_amsdu(trans, txq, dev_cmd, skb, out_meta, hdr_len, len); @@ -1065,8 +1083,8 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) iwl_wake_queue(trans, txq); } -static void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans, - struct iwl_txq *txq) +void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans, + struct iwl_txq *txq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct device *dev = trans->dev; @@ -1120,23 +1138,13 @@ static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id) clear_bit(txq_id, trans_pcie->queue_used); } -int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue_cfg_cmd *cmd, - int cmd_id, int size, - unsigned int timeout) +int iwl_trans_pcie_dyn_txq_alloc_dma(struct iwl_trans *trans, + struct iwl_txq **intxq, int size, + unsigned int timeout) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue_cfg_rsp *rsp; - struct iwl_txq *txq; - struct iwl_host_cmd hcmd = { - .id = cmd_id, - .len = { sizeof(*cmd) }, - .data = { cmd, }, - .flags = CMD_WANT_SKB, - }; - int ret, qid; - u32 wr_ptr; + int ret; + struct iwl_txq *txq; txq = kzalloc(sizeof(*txq), GFP_KERNEL); if (!txq) return -ENOMEM; @@ -1164,20 +1172,30 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, txq->wd_timeout = msecs_to_jiffies(timeout); - cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); - cmd->byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); - cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size)); + *intxq = txq; + return 0; - ret = iwl_trans_send_cmd(trans, &hcmd); - if (ret) - goto error; +error: + iwl_pcie_gen2_txq_free_memory(trans, txq); + return ret; +} + +int iwl_trans_pcie_txq_alloc_response(struct iwl_trans *trans, + struct iwl_txq *txq, + struct iwl_host_cmd *hcmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue_cfg_rsp *rsp; + int ret, qid; + u32 wr_ptr; - if (WARN_ON(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp))) { + if (WARN_ON(iwl_rx_packet_payload_len(hcmd->resp_pkt) != + sizeof(*rsp))) { ret = -EINVAL; goto error_free_resp; } - rsp = (void *)hcmd.resp_pkt->data; + rsp = (void *)hcmd->resp_pkt->data; qid = le16_to_cpu(rsp->queue_number); wr_ptr = le16_to_cpu(rsp->write_pointer); @@ -1204,11 +1222,48 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, (txq->write_ptr) | (qid << 16)); IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid); - iwl_free_resp(&hcmd); + iwl_free_resp(hcmd); return qid; error_free_resp: - iwl_free_resp(&hcmd); + iwl_free_resp(hcmd); + iwl_pcie_gen2_txq_free_memory(trans, txq); + return ret; +} + +int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, + __le16 flags, u8 sta_id, u8 tid, + int cmd_id, int size, + unsigned int timeout) +{ + struct iwl_txq *txq = NULL; + struct iwl_tx_queue_cfg_cmd cmd = { + .flags = flags, + .sta_id = sta_id, + .tid = tid, + }; + struct iwl_host_cmd hcmd = { + .id = cmd_id, + .len = { sizeof(cmd) }, + .data = { &cmd, }, + .flags = CMD_WANT_SKB, + }; + int ret; + + ret = iwl_trans_pcie_dyn_txq_alloc_dma(trans, &txq, size, timeout); + if (ret) + return ret; + + cmd.tfdq_addr = cpu_to_le64(txq->dma_addr); + cmd.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); + cmd.cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size)); + + ret = iwl_trans_send_cmd(trans, &hcmd); + if (ret) + goto error; + + return iwl_trans_pcie_txq_alloc_response(trans, txq, &hcmd); + error: iwl_pcie_gen2_txq_free_memory(trans, txq); return ret; @@ -1251,30 +1306,31 @@ void iwl_pcie_gen2_tx_free(struct iwl_trans *trans) } } -int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) +int iwl_pcie_gen2_tx_init(struct iwl_trans *trans, int txq_id, int queue_size) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *cmd_queue; - int txq_id = trans_pcie->cmd_queue, ret; + struct iwl_txq *queue; + int ret; - /* alloc and init the command queue */ + /* alloc and init the tx queue */ if (!trans_pcie->txq[txq_id]) { - cmd_queue = kzalloc(sizeof(*cmd_queue), GFP_KERNEL); - if (!cmd_queue) { - IWL_ERR(trans, "Not enough memory for command queue\n"); + queue = kzalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) { + IWL_ERR(trans, "Not enough memory for tx queue\n"); return -ENOMEM; } - trans_pcie->txq[txq_id] = cmd_queue; - ret = iwl_pcie_txq_alloc(trans, cmd_queue, TFD_CMD_SLOTS, true); + trans_pcie->txq[txq_id] = queue; + ret = iwl_pcie_txq_alloc(trans, queue, queue_size, true); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; } } else { - cmd_queue = trans_pcie->txq[txq_id]; + queue = trans_pcie->txq[txq_id]; } - ret = iwl_pcie_txq_init(trans, cmd_queue, TFD_CMD_SLOTS, true); + ret = iwl_pcie_txq_init(trans, queue, queue_size, + (txq_id == trans_pcie->cmd_queue)); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 93f0d387688a..67820bfaba64 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -17,10 +17,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -1101,7 +1097,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (!iwl_queue_used(txq, last_to_free)) { IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", + "%s: Read index for txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", __func__, txq_id, last_to_free, trans->cfg->base_params->max_tfd_queue_size, txq->write_ptr, txq->read_ptr); @@ -1188,6 +1184,10 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, lockdep_assert_held(&trans_pcie->reg_lock); + /* Make sure the NIC is still alive in the bus */ + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return -ENODEV; + if (!(cmd->flags & CMD_SEND_IN_IDLE) && !trans_pcie->ref_cmd_in_flight) { trans_pcie->ref_cmd_in_flight = true; @@ -1230,7 +1230,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, * need to be reclaimed. As result, some free space forms. If there is * enough free space (> low mark), wake the stack that feeds us. */ -static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) +void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = trans_pcie->txq[txq_id]; @@ -1912,7 +1912,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, } if (test_bit(STATUS_FW_ERROR, &trans->status)) { - iwl_trans_dump_regs(trans); + iwl_trans_pcie_dump_regs(trans); IWL_ERR(trans, "FW error in SYNC CMD %s\n", iwl_get_cmd_string(trans, cmd->id)); dump_stack(); @@ -1957,6 +1957,10 @@ cancel: int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { + /* Make sure the NIC is still alive in the bus */ + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return -ENODEV; + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && test_bit(STATUS_RFKILL_OPMODE, &trans->status)) { IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", @@ -1973,29 +1977,24 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_txq *txq, u8 hdr_len, - struct iwl_cmd_meta *out_meta, - struct iwl_device_cmd *dev_cmd, u16 tb1_len) + struct iwl_cmd_meta *out_meta) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u16 tb2_len; + u16 head_tb_len; int i; /* * Set up TFD's third entry to point directly to remainder * of skb's head, if any */ - tb2_len = skb_headlen(skb) - hdr_len; + head_tb_len = skb_headlen(skb) - hdr_len; - if (tb2_len > 0) { - dma_addr_t tb2_phys = dma_map_single(trans->dev, - skb->data + hdr_len, - tb2_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { - iwl_pcie_tfd_unmap(trans, out_meta, txq, - txq->write_ptr); + if (head_tb_len > 0) { + dma_addr_t tb_phys = dma_map_single(trans->dev, + skb->data + hdr_len, + head_tb_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) return -EINVAL; - } - iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); + iwl_pcie_txq_build_tfd(trans, txq, tb_phys, head_tb_len, false); } /* set up the remaining entries to point to the data */ @@ -2010,23 +2009,14 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, tb_phys = skb_frag_dma_map(trans->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { - iwl_pcie_tfd_unmap(trans, out_meta, txq, - txq->write_ptr); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) return -EINVAL; - } tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys, skb_frag_size(frag), false); out_meta->tbs |= BIT(tb_idx); } - trace_iwlwifi_dev_tx(trans->dev, skb, - iwl_pcie_get_tfd(trans, txq, txq->write_ptr), - trans_pcie->tfd_size, - &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, - hdr_len); - trace_iwlwifi_dev_tx_data(trans->dev, skb, hdr_len); return 0; } @@ -2087,7 +2077,6 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, u8 *start_hdr; struct iwl_tso_hdr_page *hdr_page; struct page **page_ptr; - int ret; struct tso_t tso; /* if the packet is protected, then it must be CCMP or GCMP */ @@ -2173,10 +2162,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, if (trans_pcie->sw_csum_tx) { csum_skb = alloc_skb(data_left + tcp_hdrlen(skb), GFP_ATOMIC); - if (!csum_skb) { - ret = -ENOMEM; - goto out_unmap; - } + if (!csum_skb) + return -ENOMEM; iwl_compute_pseudo_hdr_csum(iph, tcph, skb->protocol == @@ -2197,8 +2184,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, hdr_tb_len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(trans->dev, hdr_tb_phys))) { dev_kfree_skb(csum_skb); - ret = -EINVAL; - goto out_unmap; + return -EINVAL; } iwl_pcie_txq_build_tfd(trans, txq, hdr_tb_phys, hdr_tb_len, false); @@ -2223,8 +2209,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, size, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { dev_kfree_skb(csum_skb); - ret = -EINVAL; - goto out_unmap; + return -EINVAL; } iwl_pcie_txq_build_tfd(trans, txq, tb_phys, @@ -2258,10 +2243,6 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, skb_push(skb, hdr_len + iv_len); return 0; - -out_unmap: - iwl_pcie_tfd_unmap(trans, out_meta, txq, txq->write_ptr); - return ret; } #else /* CONFIG_INET */ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, @@ -2426,9 +2407,26 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, out_meta, dev_cmd, tb1_len))) goto out_err; - } else if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, - out_meta, dev_cmd, tb1_len))) { - goto out_err; + } else { + struct sk_buff *frag; + + if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, + out_meta))) + goto out_err; + + skb_walk_frags(skb, frag) { + if (unlikely(iwl_fill_data_tbs(trans, frag, txq, 0, + out_meta))) + goto out_err; + } + + trace_iwlwifi_dev_tx(trans->dev, skb, + iwl_pcie_get_tfd(trans, txq, + txq->write_ptr), + trans_pcie->tfd_size, + &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, + hdr_len); + trace_iwlwifi_dev_tx_data(trans->dev, skb, hdr_len); } /* building the A-MSDU might have changed this data, so memcpy it now */ @@ -2473,6 +2471,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, spin_unlock(&txq->lock); return 0; out_err: + iwl_pcie_tfd_unmap(trans, out_meta, txq, txq->write_ptr); spin_unlock(&txq->lock); return -1; } diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index 94ad6fe29e69..21bb68457cfe 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -73,10 +73,6 @@ #define URB_ASYNC_UNLINK 0 #endif -/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ -static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; -#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) - struct header_struct { /* 802.3 */ u8 dest[ETH_ALEN]; @@ -915,7 +911,7 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv, default: err("%s: Unexpected context state %d", __func__, state); - /* fall though */ + /* fall through */ case EZUSB_CTX_REQ_TIMEOUT: case EZUSB_CTX_REQ_FAILED: case EZUSB_CTX_RESP_TIMEOUT: diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c index e92fc5001171..789337ea676a 100644 --- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c @@ -605,9 +605,10 @@ static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, { unsigned long flags; - if (recvlength > LBS_CMD_BUFFER_SIZE) { + if (recvlength < MESSAGE_HEADER_LEN || + recvlength > LBS_CMD_BUFFER_SIZE) { lbtf_deb_usbd(&cardp->udev->dev, - "The receive buffer is too large\n"); + "The receive buffer is invalid: %d\n", recvlength); kfree_skb(skb); return; } diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 433c6a16870b..d445acc4786b 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -298,6 +298,19 @@ static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) struct mwifiex_adapter *adapter = ctx->adapter; struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { + if (card->rx_cmd_ep == ctx->ep) { + mwifiex_dbg(adapter, INFO, "%s: free rx_cmd skb\n", + __func__); + dev_kfree_skb_any(ctx->skb); + ctx->skb = NULL; + } + mwifiex_dbg(adapter, ERROR, + "%s: card removed/suspended, EP %d rx_cmd URB submit skipped\n", + __func__, ctx->ep); + return -1; + } + if (card->rx_cmd_ep != ctx->ep) { ctx->skb = dev_alloc_skb(size); if (!ctx->skb) { diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index b6c5f17dca30..7f24aad94efd 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -5,21 +5,41 @@ config MT76_USB tristate depends on MT76_CORE +config MT76x02_LIB + tristate + select MT76_CORE + +config MT76x02_USB + tristate + select MT76_USB + +config MT76x0_COMMON + tristate + select MT76x02_LIB + config MT76x2_COMMON tristate - depends on MT76_CORE + select MT76x02_LIB config MT76x0U tristate "MediaTek MT76x0U (USB) support" - select MT76_CORE + select MT76x0_COMMON + select MT76x02_USB depends on MAC80211 depends on USB help This adds support for MT7610U-based wireless USB dongles. +config MT76x0E + tristate "MediaTek MT76x0E (PCIe) support" + select MT76x0_COMMON + depends on MAC80211 + depends on PCI + help + This adds support for MT7610/MT7630-based wireless PCIe devices. + config MT76x2E tristate "MediaTek MT76x2E (PCIe) support" - select MT76_CORE select MT76x2_COMMON depends on MAC80211 depends on PCI @@ -28,9 +48,8 @@ config MT76x2E config MT76x2U tristate "MediaTek MT76x2U (USB) support" - select MT76_CORE - select MT76_USB select MT76x2_COMMON + select MT76x02_USB depends on MAC80211 depends on USB help diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile index 158d10d2716c..4d25b5c3b70b 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_MT76_CORE) += mt76.o obj-$(CONFIG_MT76_USB) += mt76-usb.o -obj-$(CONFIG_MT76x0U) += mt76x0/ +obj-$(CONFIG_MT76x0_COMMON) += mt76x0/ +obj-$(CONFIG_MT76x02_LIB) += mt76x02-lib.o +obj-$(CONFIG_MT76x02_USB) += mt76x02-usb.o obj-$(CONFIG_MT76x2_COMMON) += mt76x2-common.o obj-$(CONFIG_MT76x2E) += mt76x2e.o obj-$(CONFIG_MT76x2U) += mt76x2u.o @@ -13,10 +15,14 @@ mt76-usb-y := usb.o usb_trace.o usb_mcu.o CFLAGS_trace.o := -I$(src) CFLAGS_usb_trace.o := -I$(src) +mt76x02-lib-y := mt76x02_util.o mt76x02_mac.o mt76x02_mcu.o + +mt76x02-usb-y := mt76x02_usb_mcu.o mt76x02_usb_core.o + mt76x2-common-y := \ mt76x2_eeprom.o mt76x2_tx_common.o mt76x2_mac_common.o \ mt76x2_init_common.o mt76x2_common.o mt76x2_phy_common.o \ - mt76x2_debugfs.o + mt76x2_debugfs.o mt76x2_mcu_common.o mt76x2e-y := \ mt76x2_pci.o mt76x2_dma.o \ diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index c51da2205b93..f7fbd7016403 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -322,19 +322,13 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi) 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); + buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC); if (!buf) break; @@ -361,6 +355,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi) static void mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) { + struct page *page; void *buf; bool more; @@ -373,6 +368,13 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) skb_free_frag(buf); } while (1); spin_unlock_bh(&q->lock); + + if (!q->rx_page.va) + return; + + page = virt_to_page(q->rx_page.va); + __page_frag_cache_drain(page, q->rx_page.pagecnt_bias); + memset(&q->rx_page, 0, sizeof(q->rx_page)); } static void diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h index 27248e24a19b..357cc356342d 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.h +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -25,34 +25,6 @@ #define MT_DMA_CTL_LAST_SEC0 BIT(30) #define MT_DMA_CTL_DMA_DONE BIT(31) -#define MT_TXD_INFO_LEN GENMASK(15, 0) -#define MT_TXD_INFO_NEXT_VLD BIT(16) -#define MT_TXD_INFO_TX_BURST BIT(17) -#define MT_TXD_INFO_80211 BIT(19) -#define MT_TXD_INFO_TSO BIT(20) -#define MT_TXD_INFO_CSO BIT(21) -#define MT_TXD_INFO_WIV BIT(24) -#define MT_TXD_INFO_QSEL GENMASK(26, 25) -#define MT_TXD_INFO_DPORT GENMASK(29, 27) -#define MT_TXD_INFO_TYPE GENMASK(31, 30) - -#define MT_RX_FCE_INFO_LEN GENMASK(13, 0) -#define MT_RX_FCE_INFO_SELF_GEN BIT(15) -#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16) -#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20) -#define MT_RX_FCE_INFO_PCIE_INTR BIT(24) -#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25) -#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27) -#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30) - -/* MCU request message header */ -#define MT_MCU_MSG_LEN GENMASK(15, 0) -#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16) -#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20) -#define MT_MCU_MSG_PORT GENMASK(29, 27) -#define MT_MCU_MSG_TYPE GENMASK(31, 30) -#define MT_MCU_MSG_TYPE_CMD BIT(30) - #define MT_DMA_HDR_LEN 4 #define MT_RX_INFO_LEN 4 #define MT_FCE_INFO_LEN 4 @@ -65,14 +37,21 @@ struct mt76_desc { __le32 info; } __packed __aligned(4); -enum dma_msg_port { - WLAN_PORT, - CPU_RX_PORT, - CPU_TX_PORT, - HOST_PORT, - VIRTUAL_CPU_RX_PORT, - VIRTUAL_CPU_TX_PORT, - DISCARD, +enum mt76_qsel { + MT_QSEL_MGMT, + MT_QSEL_HCCA, + MT_QSEL_EDCA, + MT_QSEL_EDCA_2, +}; + +enum mt76_mcu_evt_type { + EVT_CMD_DONE, + EVT_CMD_ERROR, + EVT_CMD_RETRY, + EVT_EVENT_PWR_RSP, + EVT_EVENT_WOW_RSP, + EVT_EVENT_CARRIER_DETECT_RSP, + EVT_EVENT_DFS_DETECT_RSP, }; int mt76_dma_attach(struct mt76_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 029d54bce9e8..639cbafc1d50 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -283,6 +283,7 @@ mt76_alloc_device(unsigned int size, const struct ieee80211_ops *ops) spin_lock_init(&dev->rx_lock); spin_lock_init(&dev->lock); spin_lock_init(&dev->cc_lock); + mutex_init(&dev->mutex); init_waitqueue_head(&dev->tx_wait); return dev; @@ -305,6 +306,8 @@ int mt76_register_device(struct mt76_dev *dev, bool vht, wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy->available_antennas_tx = dev->antenna_mask; wiphy->available_antennas_rx = dev->antenna_mask; @@ -472,7 +475,7 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, } EXPORT_SYMBOL(mt76_wcid_key_setup); -static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb) +struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct mt76_rx_status mstat; @@ -497,6 +500,7 @@ static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb) return wcid_to_sta(mstat.wcid); } +EXPORT_SYMBOL(mt76_rx_convert); static int mt76_check_ccmp_pn(struct sk_buff *skb) diff --git a/drivers/net/wireless/mediatek/mt76/mmio.c b/drivers/net/wireless/mediatek/mt76/mmio.c index 09a14dead6e3..2f09f451a9b6 100644 --- a/drivers/net/wireless/mediatek/mt76/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mmio.c @@ -21,7 +21,7 @@ static u32 mt76_mmio_rr(struct mt76_dev *dev, u32 offset) { u32 val; - val = ioread32(dev->regs + offset); + val = ioread32(dev->mmio.regs + offset); trace_reg_rr(dev, offset, val); return val; @@ -30,7 +30,7 @@ static u32 mt76_mmio_rr(struct mt76_dev *dev, u32 offset) static void mt76_mmio_wr(struct mt76_dev *dev, u32 offset, u32 val) { trace_reg_wr(dev, offset, val); - iowrite32(val, dev->regs + offset); + iowrite32(val, dev->mmio.regs + offset); } static u32 mt76_mmio_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val) @@ -43,7 +43,7 @@ static u32 mt76_mmio_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val) static void mt76_mmio_copy(struct mt76_dev *dev, u32 offset, const void *data, int len) { - __iowrite32_copy(dev->regs + offset, data, len >> 2); + __iowrite32_copy(dev->mmio.regs + offset, data, len >> 2); } void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs) @@ -56,6 +56,10 @@ void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs) }; dev->bus = &mt76_mmio_ops; - dev->regs = regs; + dev->mmio.regs = regs; + + skb_queue_head_init(&dev->mmio.mcu.res_q); + init_waitqueue_head(&dev->mmio.mcu.wait); + mutex_init(&dev->mmio.mcu.mutex); } EXPORT_SYMBOL_GPL(mt76_mmio_init); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 2eab35879163..dbda49243a10 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -33,12 +33,21 @@ struct mt76_dev; struct mt76_wcid; +struct mt76_reg_pair { + u32 reg; + u32 value; +}; + struct mt76_bus_ops { u32 (*rr)(struct mt76_dev *dev, u32 offset); void (*wr)(struct mt76_dev *dev, u32 offset, u32 val); u32 (*rmw)(struct mt76_dev *dev, u32 offset, u32 mask, u32 val); void (*copy)(struct mt76_dev *dev, u32 offset, const void *data, int len); + int (*wr_rp)(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *rp, int len); + int (*rd_rp)(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *rp, int len); }; enum mt76_txq_id { @@ -112,6 +121,17 @@ struct mt76_queue { dma_addr_t desc_dma; struct sk_buff *rx_head; + struct page_frag_cache rx_page; +}; + +struct mt76_mcu_ops { + struct sk_buff *(*mcu_msg_alloc)(const void *data, int len); + int (*mcu_send_msg)(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp); + int (*mcu_wr_rp)(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *rp, int len); + int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *rp, int len); }; struct mt76_queue_ops { @@ -143,6 +163,8 @@ enum mt76_wcid_flags { MT_WCID_FLAG_PS, }; +#define MT76_N_WCIDS 128 + struct mt76_wcid { struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS]; @@ -210,7 +232,6 @@ enum { MT76_OFFCHANNEL, MT76_REMOVED, MT76_READING_STATS, - MT76_MORE_STATS, }; struct mt76_hw_cap { @@ -240,6 +261,8 @@ struct mt76_driver_ops { void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps); + s8 (*get_max_txpwr_adj)(struct mt76_dev *dev, + const struct ieee80211_tx_rate *rate); }; struct mt76_channel_state { @@ -307,9 +330,27 @@ struct mt76_usb { struct completion cmpl; struct mt76u_buf res; u32 msg_seq; + + /* multiple reads */ + struct mt76_reg_pair *rp; + int rp_len; + u32 base; + bool burst; } mcu; }; +struct mt76_mmio { + struct mt76e_mcu { + struct mutex mutex; + + wait_queue_head_t wait; + struct sk_buff_head res_q; + + u32 msg_seq; + } mcu; + void __iomem *regs; +}; + struct mt76_dev { struct ieee80211_hw *hw; struct cfg80211_chan_def chandef; @@ -317,9 +358,12 @@ struct mt76_dev { spinlock_t lock; spinlock_t cc_lock; + + struct mutex mutex; + const struct mt76_bus_ops *bus; const struct mt76_driver_ops *drv; - void __iomem *regs; + const struct mt76_mcu_ops *mcu_ops; struct device *dev; struct net_device napi_dev; @@ -334,6 +378,11 @@ struct mt76_dev { wait_queue_head_t tx_wait; + unsigned long wcid_mask[MT76_N_WCIDS / BITS_PER_LONG]; + + struct mt76_wcid global_wcid; + struct mt76_wcid __rcu *wcid[MT76_N_WCIDS]; + u8 macaddr[ETH_ALEN]; u32 rev; unsigned long state; @@ -353,7 +402,12 @@ struct mt76_dev { bool led_al; u8 led_pin; - struct mt76_usb usb; + u32 rxfilter; + + union { + struct mt76_mmio mmio; + struct mt76_usb usb; + }; }; enum mt76_phy_type { @@ -399,10 +453,23 @@ struct mt76_rx_status { s8 chain_signal[IEEE80211_MAX_CHAINS]; }; +#define __mt76_rr(dev, ...) (dev)->bus->rr((dev), __VA_ARGS__) +#define __mt76_wr(dev, ...) (dev)->bus->wr((dev), __VA_ARGS__) +#define __mt76_rmw(dev, ...) (dev)->bus->rmw((dev), __VA_ARGS__) +#define __mt76_wr_copy(dev, ...) (dev)->bus->copy((dev), __VA_ARGS__) + +#define __mt76_set(dev, offset, val) __mt76_rmw(dev, offset, 0, val) +#define __mt76_clear(dev, offset, val) __mt76_rmw(dev, offset, val, 0) + #define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__) #define mt76_wr(dev, ...) (dev)->mt76.bus->wr(&((dev)->mt76), __VA_ARGS__) #define mt76_rmw(dev, ...) (dev)->mt76.bus->rmw(&((dev)->mt76), __VA_ARGS__) #define mt76_wr_copy(dev, ...) (dev)->mt76.bus->copy(&((dev)->mt76), __VA_ARGS__) +#define mt76_wr_rp(dev, ...) (dev)->mt76.bus->wr_rp(&((dev)->mt76), __VA_ARGS__) +#define mt76_rd_rp(dev, ...) (dev)->mt76.bus->rd_rp(&((dev)->mt76), __VA_ARGS__) + +#define mt76_mcu_msg_alloc(dev, ...) (dev)->mt76.mcu_ops->mcu_msg_alloc(__VA_ARGS__) +#define mt76_mcu_send_msg(dev, ...) (dev)->mt76.mcu_ops->mcu_send_msg(&((dev)->mt76), __VA_ARGS__) #define mt76_set(dev, offset, val) mt76_rmw(dev, offset, 0, val) #define mt76_clear(dev, offset, val) mt76_rmw(dev, offset, val, 0) @@ -413,6 +480,9 @@ struct mt76_rx_status { #define mt76_rmw_field(_dev, _reg, _field, _val) \ mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val)) +#define __mt76_rmw_field(_dev, _reg, _field, _val) \ + __mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val)) + #define mt76_hw(dev) (dev)->mt76.hw bool __mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val, @@ -485,13 +555,7 @@ static inline int mt76_decr(int val, int size) return (val - 1) & (size - 1); } -/* Hardware uses mirrored order of queues with Q3 - * having the highest priority - */ -static inline u8 q2hwq(u8 q) -{ - return q ^ 0x3; -} +u8 mt76_ac_to_hwq(u8 ac); static inline struct ieee80211_txq * mtxq_to_txq(struct mt76_txq *mtxq) @@ -543,6 +607,8 @@ void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid); void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, struct ieee80211_key_conf *key); +struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb); + /* internal */ void mt76_tx_free(struct mt76_dev *dev); struct mt76_txwi_cache *mt76_get_txwi(struct mt76_dev *dev); @@ -599,15 +665,9 @@ int mt76u_alloc_queues(struct mt76_dev *dev); void mt76u_stop_queues(struct mt76_dev *dev); void mt76u_stop_stat_wk(struct mt76_dev *dev); void mt76u_queues_deinit(struct mt76_dev *dev); -int mt76u_skb_dma_info(struct sk_buff *skb, int port, u32 flags); -int mt76u_mcu_fw_send_data(struct mt76_dev *dev, const void *data, - int data_len, u32 max_payload, u32 offset); void mt76u_mcu_complete_urb(struct urb *urb); -struct sk_buff *mt76u_mcu_msg_alloc(const void *data, int len); -int mt76u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb, - int cmd, bool wait_resp); -void mt76u_mcu_fw_reset(struct mt76_dev *dev); int mt76u_mcu_init_rx(struct mt76_dev *dev); +void mt76u_mcu_deinit(struct mt76_dev *dev); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile b/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile index 7843908261ba..48f7979e36ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile @@ -1,7 +1,12 @@ -obj-$(CONFIG_MT76x0U) += mt76x0.o +obj-$(CONFIG_MT76x0U) += mt76x0u.o +obj-$(CONFIG_MT76x0E) += mt76x0e.o +obj-$(CONFIG_MT76x0_COMMON) += mt76x0-common.o + +mt76x0-common-y := \ + init.o main.o trace.o eeprom.o phy.o \ + mac.o debugfs.o tx.o +mt76x0u-y := usb.o +mt76x0e-y := pci.o -mt76x0-objs = \ - usb.o init.o main.o mcu.o trace.o dma.o eeprom.o phy.o \ - mac.o util.o debugfs.o tx.o core.o # ccflags-y := -DDEBUG CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/core.c b/drivers/net/wireless/mediatek/mt76/mt76x0/core.c deleted file mode 100644 index 892803fce842..000000000000 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/core.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> - * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * 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. - */ - -#include "mt76x0.h" - -int mt76x0_wait_asic_ready(struct mt76x0_dev *dev) -{ - int i = 100; - u32 val; - - do { - if (test_bit(MT76_REMOVED, &dev->mt76.state)) - return -EIO; - - val = mt76_rr(dev, MT_MAC_CSR0); - if (val && ~val) - return 0; - - udelay(10); - } while (i--); - - return -EIO; -} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/dma.c b/drivers/net/wireless/mediatek/mt76/mt76x0/dma.c deleted file mode 100644 index e2efb430419b..000000000000 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/dma.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> - * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * 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. - */ - -#include "mt76x0.h" -#include "dma.h" -#include "usb.h" -#include "trace.h" - -static int mt76x0_submit_rx_buf(struct mt76x0_dev *dev, - struct mt76x0_dma_buf_rx *e, gfp_t gfp); - -static unsigned int ieee80211_get_hdrlen_from_buf(const u8 *data, unsigned len) -{ - const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)data; - unsigned int hdrlen; - - if (unlikely(len < 10)) - return 0; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (unlikely(hdrlen > len)) - return 0; - return hdrlen; -} - -static struct sk_buff * -mt76x0_rx_skb_from_seg(struct mt76x0_dev *dev, struct mt76x0_rxwi *rxwi, - void *data, u32 seg_len, u32 truesize, struct page *p) -{ - struct sk_buff *skb; - u32 true_len, hdr_len = 0, copy, frag; - - skb = alloc_skb(p ? 128 : seg_len, GFP_ATOMIC); - if (!skb) - return NULL; - - true_len = mt76x0_mac_process_rx(dev, skb, data, rxwi); - if (!true_len || true_len > seg_len) - goto bad_frame; - - hdr_len = ieee80211_get_hdrlen_from_buf(data, true_len); - if (!hdr_len) - goto bad_frame; - - if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) { - memcpy(skb_put(skb, hdr_len), data, hdr_len); - - data += hdr_len + 2; - true_len -= hdr_len; - hdr_len = 0; - } - - /* If not doing paged RX allocated skb will always have enough space */ - copy = (true_len <= skb_tailroom(skb)) ? true_len : hdr_len + 8; - frag = true_len - copy; - - memcpy(skb_put(skb, copy), data, copy); - data += copy; - - if (frag) { - skb_add_rx_frag(skb, 0, p, data - page_address(p), - frag, truesize); - get_page(p); - } - - return skb; - -bad_frame: - dev_err_ratelimited(dev->mt76.dev, "Error: incorrect frame len:%u hdr:%u\n", - true_len, hdr_len); - dev_kfree_skb(skb); - return NULL; -} - -static void mt76x0_rx_process_seg(struct mt76x0_dev *dev, u8 *data, - u32 seg_len, struct page *p) -{ - struct sk_buff *skb; - struct mt76x0_rxwi *rxwi; - u32 fce_info, truesize = seg_len; - - /* DMA_INFO field at the beginning of the segment contains only some of - * the information, we need to read the FCE descriptor from the end. - */ - fce_info = get_unaligned_le32(data + seg_len - MT_FCE_INFO_LEN); - seg_len -= MT_FCE_INFO_LEN; - - data += MT_DMA_HDR_LEN; - seg_len -= MT_DMA_HDR_LEN; - - rxwi = (struct mt76x0_rxwi *) data; - data += sizeof(struct mt76x0_rxwi); - seg_len -= sizeof(struct mt76x0_rxwi); - - if (unlikely(FIELD_GET(MT_RXD_INFO_TYPE, fce_info))) - dev_err_once(dev->mt76.dev, "Error: RX path seen a non-pkt urb\n"); - - trace_mt76x0_rx(&dev->mt76, rxwi, fce_info); - - skb = mt76x0_rx_skb_from_seg(dev, rxwi, data, seg_len, truesize, p); - if (!skb) - return; - - spin_lock(&dev->mac_lock); - ieee80211_rx(dev->mt76.hw, skb); - spin_unlock(&dev->mac_lock); -} - -static u16 mt76x0_rx_next_seg_len(u8 *data, u32 data_len) -{ - u32 min_seg_len = MT_DMA_HDR_LEN + MT_RX_INFO_LEN + - sizeof(struct mt76x0_rxwi) + MT_FCE_INFO_LEN; - u16 dma_len = get_unaligned_le16(data); - - if (data_len < min_seg_len || - WARN_ON(!dma_len) || - WARN_ON(dma_len + MT_DMA_HDRS > data_len) || - WARN_ON(dma_len & 0x3)) - return 0; - - return MT_DMA_HDRS + dma_len; -} - -static void -mt76x0_rx_process_entry(struct mt76x0_dev *dev, struct mt76x0_dma_buf_rx *e) -{ - u32 seg_len, data_len = e->urb->actual_length; - u8 *data = page_address(e->p); - struct page *new_p = NULL; - int cnt = 0; - - if (!test_bit(MT76_STATE_INITIALIZED, &dev->mt76.state)) - return; - - /* Copy if there is very little data in the buffer. */ - if (data_len > 512) - new_p = dev_alloc_pages(MT_RX_ORDER); - - while ((seg_len = mt76x0_rx_next_seg_len(data, data_len))) { - mt76x0_rx_process_seg(dev, data, seg_len, new_p ? e->p : NULL); - - data_len -= seg_len; - data += seg_len; - cnt++; - } - - if (cnt > 1) - trace_mt76x0_rx_dma_aggr(&dev->mt76, cnt, !!new_p); - - if (new_p) { - /* we have one extra ref from the allocator */ - __free_pages(e->p, MT_RX_ORDER); - - e->p = new_p; - } -} - -static struct mt76x0_dma_buf_rx * -mt76x0_rx_get_pending_entry(struct mt76x0_dev *dev) -{ - struct mt76x0_rx_queue *q = &dev->rx_q; - struct mt76x0_dma_buf_rx *buf = NULL; - unsigned long flags; - - spin_lock_irqsave(&dev->rx_lock, flags); - - if (!q->pending) - goto out; - - buf = &q->e[q->start]; - q->pending--; - q->start = (q->start + 1) % q->entries; -out: - spin_unlock_irqrestore(&dev->rx_lock, flags); - - return buf; -} - -static void mt76x0_complete_rx(struct urb *urb) -{ - struct mt76x0_dev *dev = urb->context; - struct mt76x0_rx_queue *q = &dev->rx_q; - unsigned long flags; - - spin_lock_irqsave(&dev->rx_lock, flags); - - if (mt76x0_urb_has_error(urb)) - dev_err(dev->mt76.dev, "Error: RX urb failed:%d\n", urb->status); - if (WARN_ONCE(q->e[q->end].urb != urb, "RX urb mismatch")) - goto out; - - q->end = (q->end + 1) % q->entries; - q->pending++; - tasklet_schedule(&dev->rx_tasklet); -out: - spin_unlock_irqrestore(&dev->rx_lock, flags); -} - -static void mt76x0_rx_tasklet(unsigned long data) -{ - struct mt76x0_dev *dev = (struct mt76x0_dev *) data; - struct mt76x0_dma_buf_rx *e; - - while ((e = mt76x0_rx_get_pending_entry(dev))) { - if (e->urb->status) - continue; - - mt76x0_rx_process_entry(dev, e); - mt76x0_submit_rx_buf(dev, e, GFP_ATOMIC); - } -} - -static void mt76x0_complete_tx(struct urb *urb) -{ - struct mt76x0_tx_queue *q = urb->context; - struct mt76x0_dev *dev = q->dev; - struct sk_buff *skb; - unsigned long flags; - - spin_lock_irqsave(&dev->tx_lock, flags); - - if (mt76x0_urb_has_error(urb)) - dev_err(dev->mt76.dev, "Error: TX urb failed:%d\n", urb->status); - if (WARN_ONCE(q->e[q->start].urb != urb, "TX urb mismatch")) - goto out; - - skb = q->e[q->start].skb; - trace_mt76x0_tx_dma_done(&dev->mt76, skb); - - __skb_queue_tail(&dev->tx_skb_done, skb); - tasklet_schedule(&dev->tx_tasklet); - - if (q->used == q->entries - q->entries / 8) - ieee80211_wake_queue(dev->mt76.hw, skb_get_queue_mapping(skb)); - - q->start = (q->start + 1) % q->entries; - q->used--; -out: - spin_unlock_irqrestore(&dev->tx_lock, flags); -} - -static void mt76x0_tx_tasklet(unsigned long data) -{ - struct mt76x0_dev *dev = (struct mt76x0_dev *) data; - struct sk_buff_head skbs; - unsigned long flags; - - __skb_queue_head_init(&skbs); - - spin_lock_irqsave(&dev->tx_lock, flags); - - set_bit(MT76_MORE_STATS, &dev->mt76.state); - if (!test_and_set_bit(MT76_READING_STATS, &dev->mt76.state)) - queue_delayed_work(dev->stat_wq, &dev->stat_work, - msecs_to_jiffies(10)); - - skb_queue_splice_init(&dev->tx_skb_done, &skbs); - - spin_unlock_irqrestore(&dev->tx_lock, flags); - - while (!skb_queue_empty(&skbs)) { - struct sk_buff *skb = __skb_dequeue(&skbs); - - mt76x0_tx_status(dev, skb); - } -} - -static int mt76x0_dma_submit_tx(struct mt76x0_dev *dev, - struct sk_buff *skb, u8 ep) -{ - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - unsigned snd_pipe = usb_sndbulkpipe(usb_dev, dev->out_ep[ep]); - struct mt76x0_dma_buf_tx *e; - struct mt76x0_tx_queue *q = &dev->tx_q[ep]; - unsigned long flags; - int ret; - - spin_lock_irqsave(&dev->tx_lock, flags); - - if (WARN_ON_ONCE(q->entries <= q->used)) { - ret = -ENOSPC; - goto out; - } - - e = &q->e[q->end]; - e->skb = skb; - usb_fill_bulk_urb(e->urb, usb_dev, snd_pipe, skb->data, skb->len, - mt76x0_complete_tx, q); - ret = usb_submit_urb(e->urb, GFP_ATOMIC); - if (ret) { - /* Special-handle ENODEV from TX urb submission because it will - * often be the first ENODEV we see after device is removed. - */ - if (ret == -ENODEV) - set_bit(MT76_REMOVED, &dev->mt76.state); - else - dev_err(dev->mt76.dev, "Error: TX urb submit failed:%d\n", - ret); - goto out; - } - - q->end = (q->end + 1) % q->entries; - q->used++; - - if (q->used >= q->entries) - ieee80211_stop_queue(dev->mt76.hw, skb_get_queue_mapping(skb)); -out: - spin_unlock_irqrestore(&dev->tx_lock, flags); - - return ret; -} - -/* Map USB endpoint number to Q id in the DMA engine */ -static enum mt76_qsel ep2dmaq(u8 ep) -{ - if (ep == 5) - return MT_QSEL_MGMT; - return MT_QSEL_EDCA; -} - -int mt76x0_dma_enqueue_tx(struct mt76x0_dev *dev, struct sk_buff *skb, - struct mt76_wcid *wcid, int hw_q) -{ - u8 ep = q2ep(hw_q); - u32 dma_flags; - int ret; - - dma_flags = MT_TXD_PKT_INFO_80211; - if (wcid->hw_key_idx == 0xff) - dma_flags |= MT_TXD_PKT_INFO_WIV; - - ret = mt76x0_dma_skb_wrap_pkt(skb, ep2dmaq(ep), dma_flags); - if (ret) - return ret; - - ret = mt76x0_dma_submit_tx(dev, skb, ep); - - if (ret) { - ieee80211_free_txskb(dev->mt76.hw, skb); - return ret; - } - - return 0; -} - -static void mt76x0_kill_rx(struct mt76x0_dev *dev) -{ - int i; - unsigned long flags; - - spin_lock_irqsave(&dev->rx_lock, flags); - - for (i = 0; i < dev->rx_q.entries; i++) { - int next = dev->rx_q.end; - - spin_unlock_irqrestore(&dev->rx_lock, flags); - usb_poison_urb(dev->rx_q.e[next].urb); - spin_lock_irqsave(&dev->rx_lock, flags); - } - - spin_unlock_irqrestore(&dev->rx_lock, flags); -} - -static int mt76x0_submit_rx_buf(struct mt76x0_dev *dev, - struct mt76x0_dma_buf_rx *e, gfp_t gfp) -{ - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - u8 *buf = page_address(e->p); - unsigned pipe; - int ret; - - pipe = usb_rcvbulkpipe(usb_dev, dev->in_ep[MT_EP_IN_PKT_RX]); - - usb_fill_bulk_urb(e->urb, usb_dev, pipe, buf, MT_RX_URB_SIZE, - mt76x0_complete_rx, dev); - - trace_mt76x0_submit_urb(&dev->mt76, e->urb); - ret = usb_submit_urb(e->urb, gfp); - if (ret) - dev_err(dev->mt76.dev, "Error: submit RX URB failed:%d\n", ret); - - return ret; -} - -static int mt76x0_submit_rx(struct mt76x0_dev *dev) -{ - int i, ret; - - for (i = 0; i < dev->rx_q.entries; i++) { - ret = mt76x0_submit_rx_buf(dev, &dev->rx_q.e[i], GFP_KERNEL); - if (ret) - return ret; - } - - return 0; -} - -static void mt76x0_free_rx(struct mt76x0_dev *dev) -{ - int i; - - for (i = 0; i < dev->rx_q.entries; i++) { - __free_pages(dev->rx_q.e[i].p, MT_RX_ORDER); - usb_free_urb(dev->rx_q.e[i].urb); - } -} - -static int mt76x0_alloc_rx(struct mt76x0_dev *dev) -{ - int i; - - memset(&dev->rx_q, 0, sizeof(dev->rx_q)); - dev->rx_q.dev = dev; - dev->rx_q.entries = N_RX_ENTRIES; - - for (i = 0; i < N_RX_ENTRIES; i++) { - dev->rx_q.e[i].urb = usb_alloc_urb(0, GFP_KERNEL); - dev->rx_q.e[i].p = dev_alloc_pages(MT_RX_ORDER); - - if (!dev->rx_q.e[i].urb || !dev->rx_q.e[i].p) - return -ENOMEM; - } - - return 0; -} - -static void mt76x0_free_tx_queue(struct mt76x0_tx_queue *q) -{ - int i; - - WARN_ON(q->used); - - for (i = 0; i < q->entries; i++) { - usb_poison_urb(q->e[i].urb); - usb_free_urb(q->e[i].urb); - } -} - -static void mt76x0_free_tx(struct mt76x0_dev *dev) -{ - int i; - - for (i = 0; i < __MT_EP_OUT_MAX; i++) - mt76x0_free_tx_queue(&dev->tx_q[i]); -} - -static int mt76x0_alloc_tx_queue(struct mt76x0_dev *dev, - struct mt76x0_tx_queue *q) -{ - int i; - - q->dev = dev; - q->entries = N_TX_ENTRIES; - - for (i = 0; i < N_TX_ENTRIES; i++) { - q->e[i].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!q->e[i].urb) - return -ENOMEM; - } - - return 0; -} - -static int mt76x0_alloc_tx(struct mt76x0_dev *dev) -{ - int i; - - dev->tx_q = devm_kcalloc(dev->mt76.dev, __MT_EP_OUT_MAX, - sizeof(*dev->tx_q), GFP_KERNEL); - - for (i = 0; i < __MT_EP_OUT_MAX; i++) - if (mt76x0_alloc_tx_queue(dev, &dev->tx_q[i])) - return -ENOMEM; - - return 0; -} - -int mt76x0_dma_init(struct mt76x0_dev *dev) -{ - int ret = -ENOMEM; - - tasklet_init(&dev->tx_tasklet, mt76x0_tx_tasklet, (unsigned long) dev); - tasklet_init(&dev->rx_tasklet, mt76x0_rx_tasklet, (unsigned long) dev); - - ret = mt76x0_alloc_tx(dev); - if (ret) - goto err; - ret = mt76x0_alloc_rx(dev); - if (ret) - goto err; - - ret = mt76x0_submit_rx(dev); - if (ret) - goto err; - - return 0; -err: - mt76x0_dma_cleanup(dev); - return ret; -} - -void mt76x0_dma_cleanup(struct mt76x0_dev *dev) -{ - mt76x0_kill_rx(dev); - - tasklet_kill(&dev->rx_tasklet); - - mt76x0_free_rx(dev); - mt76x0_free_tx(dev); - - tasklet_kill(&dev->tx_tasklet); -} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index 36da1e6bc21a..79856bde1632 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -123,18 +123,19 @@ mt76x0_set_chip_cap(struct mt76x0_dev *dev, u8 *eeprom) switch (FIELD_GET(MT_EE_NIC_CONF_0_BOARD_TYPE, nic_conf0)) { case BOARD_TYPE_5GHZ: - dev->ee->has_5ghz = true; + dev->mt76.cap.has_5ghz = true; break; case BOARD_TYPE_2GHZ: - dev->ee->has_2ghz = true; + dev->mt76.cap.has_2ghz = true; break; default: - dev->ee->has_2ghz = true; - dev->ee->has_5ghz = true; + dev->mt76.cap.has_2ghz = true; + dev->mt76.cap.has_5ghz = true; break; } - dev_dbg(dev->mt76.dev, "Has 2GHZ %d 5GHZ %d\n", dev->ee->has_2ghz, dev->ee->has_5ghz); + dev_dbg(dev->mt76.dev, "Has 2GHZ %d 5GHZ %d\n", + dev->mt76.cap.has_2ghz, dev->mt76.cap.has_5ghz); if (!field_valid(nic_conf1 & 0xff)) nic_conf1 &= 0xff00; @@ -159,18 +160,19 @@ static int mt76x0_set_macaddr(struct mt76x0_dev *dev, const u8 *eeprom) { const void *src = eeprom + MT_EE_MAC_ADDR; + u8 *dst = dev->mt76.macaddr; - ether_addr_copy(dev->macaddr, src); + ether_addr_copy(dev->mt76.macaddr, src); - if (!is_valid_ether_addr(dev->macaddr)) { - eth_random_addr(dev->macaddr); + if (!is_valid_ether_addr(dst)) { + eth_random_addr(dst); dev_info(dev->mt76.dev, "Invalid MAC address, using random address %pM\n", - dev->macaddr); + dst); } - mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr)); - mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4) | + mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dst)); + mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dst + 4) | FIELD_PREP(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff)); return 0; @@ -443,3 +445,5 @@ out: kfree(eeprom); return ret; } + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h index e37b573aed7b..cd0f14361405 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h @@ -112,9 +112,6 @@ struct mt76x0_eeprom_params { u8 tx_pwr_per_chan[58]; struct reg_channel_bounds reg; - - bool has_2ghz; - bool has_5ghz; }; int mt76x0_eeprom_init(struct mt76x0_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c index 7cdb3e740522..3a88be267daf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c @@ -18,10 +18,28 @@ #include "eeprom.h" #include "trace.h" #include "mcu.h" -#include "usb.h" +#include "../mt76x02_util.h" #include "initvals.h" +static void mt76x0_vht_cap_mask(struct ieee80211_supported_band *sband) +{ + struct ieee80211_sta_vht_cap *vht_cap = &sband->vht_cap; + u16 mcs_map = 0; + int i; + + vht_cap->cap &= ~IEEE80211_VHT_CAP_RXLDPC; + for (i = 0; i < 8; i++) { + if (!i) + mcs_map |= (IEEE80211_VHT_MCS_SUPPORT_0_7 << (i * 2)); + else + mcs_map |= + (IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 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); +} + static void mt76x0_set_wlan_state(struct mt76x0_dev *dev, u32 val, bool enable) { @@ -92,6 +110,7 @@ void mt76x0_chip_onoff(struct mt76x0_dev *dev, bool enable, bool reset) mutex_unlock(&dev->hw_atomic_mutex); } +EXPORT_SYMBOL_GPL(mt76x0_chip_onoff); static void mt76x0_reset_csr_bbp(struct mt76x0_dev *dev) { @@ -113,12 +132,13 @@ static void mt76x0_init_usb_dma(struct mt76x0_dev *dev) val = mt76_rr(dev, MT_USB_DMA_CFG); - val |= FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, MT_USB_AGGR_TIMEOUT) | - FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_LMT, MT_USB_AGGR_SIZE_LIMIT) | - MT_USB_DMA_CFG_RX_BULK_EN | + val |= MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN; - if (dev->in_max_packet == 512) - val |= MT_USB_DMA_CFG_RX_BULK_AGG_EN; + + /* disable AGGR_BULK_RX in order to receive one + * frame in each rx urb and avoid copies + */ + val &= ~MT_USB_DMA_CFG_RX_BULK_AGG_EN; mt76_wr(dev, MT_USB_DMA_CFG, val); val = mt76_rr(dev, MT_COM_REG0); @@ -127,14 +147,15 @@ static void mt76x0_init_usb_dma(struct mt76x0_dev *dev) val = mt76_rr(dev, MT_USB_DMA_CFG); - val |= MT_USB_DMA_CFG_RX_DROP_OR_PADDING; + val |= MT_USB_DMA_CFG_RX_DROP_OR_PAD; mt76_wr(dev, MT_USB_DMA_CFG, val); - val &= ~MT_USB_DMA_CFG_RX_DROP_OR_PADDING; + val &= ~MT_USB_DMA_CFG_RX_DROP_OR_PAD; mt76_wr(dev, MT_USB_DMA_CFG, val); } -#define RANDOM_WRITE(dev, tab) \ - mt76x0_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, tab, ARRAY_SIZE(tab)); +#define RANDOM_WRITE(dev, tab) \ + mt76_wr_rp(dev, MT_MCU_MEMMAP_WLAN, \ + tab, ARRAY_SIZE(tab)) static int mt76x0_init_bbp(struct mt76x0_dev *dev) { @@ -227,59 +248,54 @@ static void mt76x0_init_mac_registers(struct mt76x0_dev *dev) static int mt76x0_init_wcid_mem(struct mt76x0_dev *dev) { u32 *vals; - int i, ret; + int i; - vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL); + vals = kmalloc(sizeof(*vals) * MT76_N_WCIDS * 2, GFP_KERNEL); if (!vals) return -ENOMEM; - for (i = 0; i < N_WCIDS; i++) { + for (i = 0; i < MT76_N_WCIDS; i++) { vals[i * 2] = 0xffffffff; vals[i * 2 + 1] = 0x00ffffff; } - ret = mt76x0_burst_write_regs(dev, MT_WCID_ADDR_BASE, - vals, N_WCIDS * 2); + mt76_wr_copy(dev, MT_WCID_ADDR_BASE, vals, MT76_N_WCIDS * 2); kfree(vals); - - return ret; + return 0; } -static int mt76x0_init_key_mem(struct mt76x0_dev *dev) +static void mt76x0_init_key_mem(struct mt76x0_dev *dev) { u32 vals[4] = {}; - return mt76x0_burst_write_regs(dev, MT_SKEY_MODE_BASE_0, - vals, ARRAY_SIZE(vals)); + mt76_wr_copy(dev, MT_SKEY_MODE_BASE_0, vals, ARRAY_SIZE(vals)); } static int mt76x0_init_wcid_attr_mem(struct mt76x0_dev *dev) { u32 *vals; - int i, ret; + int i; - vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL); + vals = kmalloc(sizeof(*vals) * MT76_N_WCIDS * 2, GFP_KERNEL); if (!vals) return -ENOMEM; - for (i = 0; i < N_WCIDS * 2; i++) + for (i = 0; i < MT76_N_WCIDS * 2; i++) vals[i] = 1; - ret = mt76x0_burst_write_regs(dev, MT_WCID_ATTR_BASE, - vals, N_WCIDS * 2); + mt76_wr_copy(dev, MT_WCID_ATTR_BASE, vals, MT76_N_WCIDS * 2); kfree(vals); - - return ret; + return 0; } static void mt76x0_reset_counters(struct mt76x0_dev *dev) { - mt76_rr(dev, MT_RX_STA_CNT0); - mt76_rr(dev, MT_RX_STA_CNT1); - mt76_rr(dev, MT_RX_STA_CNT2); - mt76_rr(dev, MT_TX_STA_CNT0); - mt76_rr(dev, MT_TX_STA_CNT1); - mt76_rr(dev, MT_TX_STA_CNT2); + mt76_rr(dev, MT_RX_STAT_0); + mt76_rr(dev, MT_RX_STAT_1); + mt76_rr(dev, MT_RX_STAT_2); + mt76_rr(dev, MT_TX_STA_0); + mt76_rr(dev, MT_TX_STA_1); + mt76_rr(dev, MT_TX_STA_2); } int mt76x0_mac_start(struct mt76x0_dev *dev) @@ -290,14 +306,14 @@ int mt76x0_mac_start(struct mt76x0_dev *dev) MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 200000)) return -ETIMEDOUT; - dev->rxfilter = MT_RX_FILTR_CFG_CRC_ERR | + dev->mt76.rxfilter = MT_RX_FILTR_CFG_CRC_ERR | MT_RX_FILTR_CFG_PHY_ERR | MT_RX_FILTR_CFG_PROMISC | MT_RX_FILTR_CFG_VER_ERR | MT_RX_FILTR_CFG_DUP | MT_RX_FILTR_CFG_CFACK | MT_RX_FILTR_CFG_CFEND | MT_RX_FILTR_CFG_ACK | MT_RX_FILTR_CFG_CTS | MT_RX_FILTR_CFG_RTS | MT_RX_FILTR_CFG_PSPOLL | MT_RX_FILTR_CFG_BA | MT_RX_FILTR_CFG_CTRL_RSV; - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX); @@ -359,15 +375,12 @@ static void mt76x0_mac_stop_hw(struct mt76x0_dev *dev) void mt76x0_mac_stop(struct mt76x0_dev *dev) { + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mac_work); + mt76u_stop_stat_wk(&dev->mt76); mt76x0_mac_stop_hw(dev); - flush_delayed_work(&dev->stat_work); - cancel_delayed_work_sync(&dev->stat_work); -} - -static void mt76x0_stop_hardware(struct mt76x0_dev *dev) -{ - mt76x0_chip_onoff(dev, false, false); } +EXPORT_SYMBOL_GPL(mt76x0_mac_stop); int mt76x0_init_hardware(struct mt76x0_dev *dev) { @@ -382,26 +395,14 @@ int mt76x0_init_hardware(struct mt76x0_dev *dev) dev->beacon_offsets = beacon_offsets; - mt76x0_chip_onoff(dev, true, true); - - ret = mt76x0_wait_asic_ready(dev); - if (ret) - goto err; - ret = mt76x0_mcu_init(dev); - if (ret) - goto err; - if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | - MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) { - ret = -EIO; - goto err; - } + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) + return -EIO; /* Wait for ASIC ready after FW load. */ - ret = mt76x0_wait_asic_ready(dev); - if (ret) - goto err; + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; mt76x0_reset_csr_bbp(dev); mt76x0_init_usb_dma(dev); @@ -409,34 +410,29 @@ int mt76x0_init_hardware(struct mt76x0_dev *dev) mt76_wr(dev, MT_HEADER_TRANS_CTRL_REG, 0x0); mt76_wr(dev, MT_TSO_CTRL, 0x0); - ret = mt76x0_mcu_cmd_init(dev); + ret = mt76x02_mcu_function_select(&dev->mt76, Q_SELECT, 1, false); if (ret) - goto err; - ret = mt76x0_dma_init(dev); - if (ret) - goto err_mcu; + return ret; mt76x0_init_mac_registers(dev); if (!mt76_poll_msec(dev, MT_MAC_STATUS, - MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, 0, 1000)) { - ret = -EIO; - goto err_rx; - } + MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, 0, 1000)) + return -EIO; ret = mt76x0_init_bbp(dev); if (ret) - goto err_rx; + return ret; ret = mt76x0_init_wcid_mem(dev); if (ret) - goto err_rx; - ret = mt76x0_init_key_mem(dev); - if (ret) - goto err_rx; + return ret; + + mt76x0_init_key_mem(dev); + ret = mt76x0_init_wcid_attr_mem(dev); if (ret) - goto err_rx; + return ret; mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | MT_BEACON_TIME_CFG_SYNC_MODE | @@ -453,268 +449,100 @@ int mt76x0_init_hardware(struct mt76x0_dev *dev) ret = mt76x0_eeprom_init(dev); if (ret) - goto err_rx; + return ret; mt76x0_phy_init(dev); - return 0; -err_rx: - mt76x0_dma_cleanup(dev); -err_mcu: - mt76x0_mcu_cmd_deinit(dev); -err: - mt76x0_chip_onoff(dev, false, false); - return ret; + return 0; } +EXPORT_SYMBOL_GPL(mt76x0_init_hardware); void mt76x0_cleanup(struct mt76x0_dev *dev) { - if (!test_and_clear_bit(MT76_STATE_INITIALIZED, &dev->mt76.state)) - return; - - mt76x0_stop_hardware(dev); - mt76x0_dma_cleanup(dev); - mt76x0_mcu_cmd_deinit(dev); + clear_bit(MT76_STATE_INITIALIZED, &dev->mt76.state); + mt76x0_chip_onoff(dev, false, false); + mt76u_queues_deinit(&dev->mt76); + mt76u_mcu_deinit(&dev->mt76); } +EXPORT_SYMBOL_GPL(mt76x0_cleanup); -struct mt76x0_dev *mt76x0_alloc_device(struct device *pdev) +struct mt76x0_dev * +mt76x0_alloc_device(struct device *pdev, const struct mt76_driver_ops *drv_ops) { - struct ieee80211_hw *hw; struct mt76x0_dev *dev; + struct mt76_dev *mdev; - hw = ieee80211_alloc_hw(sizeof(*dev), &mt76x0_ops); - if (!hw) + mdev = mt76_alloc_device(sizeof(*dev), &mt76x0_ops); + if (!mdev) return NULL; - dev = hw->priv; - dev->mt76.dev = pdev; - dev->mt76.hw = hw; - mutex_init(&dev->usb_ctrl_mtx); + mdev->dev = pdev; + mdev->drv = drv_ops; + + dev = container_of(mdev, struct mt76x0_dev, mt76); mutex_init(&dev->reg_atomic_mutex); mutex_init(&dev->hw_atomic_mutex); - mutex_init(&dev->mutex); - spin_lock_init(&dev->tx_lock); - spin_lock_init(&dev->rx_lock); - spin_lock_init(&dev->mt76.lock); spin_lock_init(&dev->mac_lock); spin_lock_init(&dev->con_mon_lock); atomic_set(&dev->avg_ampdu_len, 1); - skb_queue_head_init(&dev->tx_skb_done); - - dev->stat_wq = alloc_workqueue("mt76x0", WQ_UNBOUND, 0); - if (!dev->stat_wq) { - ieee80211_free_hw(hw); - return NULL; - } return dev; } - -#define CHAN2G(_idx, _freq) { \ - .band = NL80211_BAND_2GHZ, \ - .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), -}; - -#define CHAN5G(_idx, _freq) { \ - .band = NL80211_BAND_5GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 30, \ -} - -static const struct ieee80211_channel mt76_channels_5ghz[] = { - CHAN5G(36, 5180), - CHAN5G(40, 5200), - CHAN5G(44, 5220), - CHAN5G(46, 5230), - 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), -}; - -#define CCK_RATE(_idx, _rate) { \ - .bitrate = _rate, \ - .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ - .hw_value = (MT_PHY_TYPE_CCK << 8) | _idx, \ - .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + _idx), \ -} - -#define OFDM_RATE(_idx, _rate) { \ - .bitrate = _rate, \ - .hw_value = (MT_PHY_TYPE_OFDM << 8) | _idx, \ - .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | _idx, \ -} - -static struct ieee80211_rate mt76_rates[] = { - CCK_RATE(0, 10), - CCK_RATE(1, 20), - CCK_RATE(2, 55), - CCK_RATE(3, 110), - OFDM_RATE(0, 60), - OFDM_RATE(1, 90), - OFDM_RATE(2, 120), - OFDM_RATE(3, 180), - OFDM_RATE(4, 240), - OFDM_RATE(5, 360), - OFDM_RATE(6, 480), - OFDM_RATE(7, 540), -}; - -static int -mt76_init_sband(struct mt76x0_dev *dev, struct ieee80211_supported_band *sband, - const struct ieee80211_channel *chan, int n_chan, - struct ieee80211_rate *rates, int n_rates) -{ - struct ieee80211_sta_ht_cap *ht_cap; - void *chanlist; - int size; - - size = n_chan * sizeof(*chan); - chanlist = devm_kmemdup(dev->mt76.dev, chan, size, GFP_KERNEL); - if (!chanlist) - return -ENOMEM; - - sband->channels = chanlist; - sband->n_channels = n_chan; - sband->bitrates = rates; - sband->n_bitrates = n_rates; - - 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 | - (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); - - ht_cap->mcs.rx_mask[0] = 0xff; - ht_cap->mcs.rx_mask[4] = 0x1; - 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_2; - - return 0; -} - -static int -mt76_init_sband_2g(struct mt76x0_dev *dev) -{ - dev->mt76.hw->wiphy->bands[NL80211_BAND_2GHZ] = &dev->mt76.sband_2g.sband; - - WARN_ON(dev->ee->reg.start - 1 + dev->ee->reg.num > - ARRAY_SIZE(mt76_channels_2ghz)); - - - return mt76_init_sband(dev, &dev->mt76.sband_2g.sband, - mt76_channels_2ghz, ARRAY_SIZE(mt76_channels_2ghz), - mt76_rates, ARRAY_SIZE(mt76_rates)); -} - -static int -mt76_init_sband_5g(struct mt76x0_dev *dev) -{ - dev->mt76.hw->wiphy->bands[NL80211_BAND_5GHZ] = &dev->mt76.sband_5g.sband; - - return mt76_init_sband(dev, &dev->mt76.sband_5g.sband, - mt76_channels_5ghz, ARRAY_SIZE(mt76_channels_5ghz), - mt76_rates + 4, ARRAY_SIZE(mt76_rates) - 4); -} - +EXPORT_SYMBOL_GPL(mt76x0_alloc_device); int mt76x0_register_device(struct mt76x0_dev *dev) { - struct ieee80211_hw *hw = dev->mt76.hw; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_hw *hw = mdev->hw; struct wiphy *wiphy = hw->wiphy; int ret; + ret = mt76x0_init_hardware(dev); + if (ret) + return ret; + /* Reserve WCID 0 for mcast - thanks to this APs WCID will go to * entry no. 1 like it does in the vendor driver. */ - dev->wcid_mask[0] |= 1; + mdev->wcid_mask[0] |= 1; /* init fake wcid for monitor interfaces */ - dev->mon_wcid = devm_kmalloc(dev->mt76.dev, sizeof(*dev->mon_wcid), - GFP_KERNEL); - if (!dev->mon_wcid) - return -ENOMEM; - dev->mon_wcid->idx = 0xff; - dev->mon_wcid->hw_key_idx = -1; + mdev->global_wcid.idx = 0xff; + mdev->global_wcid.hw_key_idx = -1; - SET_IEEE80211_DEV(hw, dev->mt76.dev); + /* init antenna configuration */ + mdev->antenna_mask = 1; hw->queues = 4; - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); hw->max_rates = 1; hw->max_report_rates = 7; hw->max_rate_tries = 1; + hw->extra_tx_headroom = sizeof(struct mt76x02_txwi) + 4 + 2; - hw->sta_data_size = sizeof(struct mt76_sta); - hw->vif_data_size = sizeof(struct mt76_vif); - - SET_IEEE80211_PERM_ADDR(hw, dev->macaddr); + hw->sta_data_size = sizeof(struct mt76x02_sta); + hw->vif_data_size = sizeof(struct mt76x02_vif); - wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - if (dev->ee->has_2ghz) { - ret = mt76_init_sband_2g(dev); - if (ret) - return ret; - } - - if (dev->ee->has_5ghz) { - ret = mt76_init_sband_5g(dev); - if (ret) - return ret; - } - - dev->mt76.chandef.chan = &dev->mt76.sband_2g.sband.channels[0]; - INIT_DELAYED_WORK(&dev->mac_work, mt76x0_mac_work); - INIT_DELAYED_WORK(&dev->stat_work, mt76x0_tx_stat); - ret = ieee80211_register_hw(hw); + ret = mt76_register_device(mdev, true, mt76x02_rates, + ARRAY_SIZE(mt76x02_rates)); if (ret) return ret; + /* overwrite unsupported features */ + if (mdev->cap.has_5ghz) + mt76x0_vht_cap_mask(&dev->mt76.sband_5g.sband); + + /* check hw sg support in order to enable AMSDU */ + if (mt76u_check_sg(mdev)) + hw->max_tx_fragments = MT_SG_MAX_SIZE; + else + hw->max_tx_fragments = 1; + mt76x0_init_debugfs(dev); return 0; } +EXPORT_SYMBOL_GPL(mt76x0_register_device); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mac.c b/drivers/net/wireless/mediatek/mt76/mt76x0/mac.c index 91a84be36d3b..f55734a922aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/mac.c @@ -15,237 +15,9 @@ #include "mt76x0.h" #include "trace.h" +#include "../mt76x02_util.h" #include <linux/etherdevice.h> -static void -mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate, - enum nl80211_band band) -{ - u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); - - txrate->idx = 0; - txrate->flags = 0; - txrate->count = 1; - - switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { - case MT_PHY_TYPE_OFDM: - if (band == NL80211_BAND_2GHZ) - idx += 4; - - txrate->idx = idx; - return; - case MT_PHY_TYPE_CCK: - if (idx >= 8) - idx -= 8; - - txrate->idx = idx; - return; - case MT_PHY_TYPE_HT_GF: - txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; - /* fall through */ - case MT_PHY_TYPE_HT: - txrate->flags |= IEEE80211_TX_RC_MCS; - txrate->idx = idx; - break; - case MT_PHY_TYPE_VHT: - txrate->flags |= IEEE80211_TX_RC_VHT_MCS; - txrate->idx = idx; - break; - default: - WARN_ON(1); - return; - } - - switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { - case MT_PHY_BW_20: - break; - case MT_PHY_BW_40: - txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - break; - case MT_PHY_BW_80: - txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; - break; - default: - WARN_ON(1); - return; - } - - if (rate & MT_RXWI_RATE_SGI) - txrate->flags |= IEEE80211_TX_RC_SHORT_GI; -} - -static void -mt76_mac_fill_tx_status(struct mt76x0_dev *dev, struct ieee80211_tx_info *info, - struct mt76_tx_status *st, int n_frames) -{ - struct ieee80211_tx_rate *rate = info->status.rates; - int cur_idx, last_rate; - int i; - - if (!n_frames) - return; - - last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); - mt76_mac_process_tx_rate(&rate[last_rate], st->rate, - dev->mt76.chandef.chan->band); - if (last_rate < IEEE80211_TX_MAX_RATES - 1) - rate[last_rate + 1].idx = -1; - - cur_idx = rate[last_rate].idx + last_rate; - for (i = 0; i <= last_rate; i++) { - rate[i].flags = rate[last_rate].flags; - rate[i].idx = max_t(int, 0, cur_idx - i); - rate[i].count = 1; - } - - rate[last_rate - 1].count = st->retry + 1 - last_rate; - - info->status.ampdu_len = n_frames; - info->status.ampdu_ack_len = st->success ? n_frames : 0; - - if (st->pktid & MT_TXWI_PKTID_PROBE) - info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; - - if (st->aggr) - info->flags |= IEEE80211_TX_CTL_AMPDU | - IEEE80211_TX_STAT_AMPDU; - - if (!st->ack_req) - info->flags |= IEEE80211_TX_CTL_NO_ACK; - else if (st->success) - info->flags |= IEEE80211_TX_STAT_ACK; -} - -u16 mt76x0_mac_tx_rate_val(struct mt76x0_dev *dev, - const struct ieee80211_tx_rate *rate, u8 *nss_val) -{ - u16 rateval; - u8 phy, rate_idx; - u8 nss = 1; - u8 bw = 0; - - if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { - rate_idx = rate->idx; - nss = 1 + (rate->idx >> 4); - phy = MT_PHY_TYPE_VHT; - if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - bw = 2; - else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - bw = 1; - } else if (rate->flags & IEEE80211_TX_RC_MCS) { - rate_idx = rate->idx; - nss = 1 + (rate->idx >> 3); - phy = MT_PHY_TYPE_HT; - if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) - phy = MT_PHY_TYPE_HT_GF; - if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - bw = 1; - } else { - const struct ieee80211_rate *r; - int band = dev->mt76.chandef.chan->band; - u16 val; - - r = &dev->mt76.hw->wiphy->bands[band]->bitrates[rate->idx]; - if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - val = r->hw_value_short; - else - val = r->hw_value; - - phy = val >> 8; - rate_idx = val & 0xff; - bw = 0; - } - - rateval = FIELD_PREP(MT_RXWI_RATE_INDEX, rate_idx); - rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); - rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); - if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - rateval |= MT_RXWI_RATE_SGI; - - *nss_val = nss; - return cpu_to_le16(rateval); -} - -void mt76x0_mac_wcid_set_rate(struct mt76x0_dev *dev, struct mt76_wcid *wcid, - const struct ieee80211_tx_rate *rate) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->mt76.lock, flags); - wcid->tx_rate = mt76x0_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss); - wcid->tx_rate_set = true; - spin_unlock_irqrestore(&dev->mt76.lock, flags); -} - -struct mt76_tx_status mt76x0_mac_fetch_tx_status(struct mt76x0_dev *dev) -{ - struct mt76_tx_status stat = {}; - u32 stat2, stat1; - - stat2 = mt76_rr(dev, MT_TX_STAT_FIFO_EXT); - stat1 = mt76_rr(dev, MT_TX_STAT_FIFO); - - stat.valid = !!(stat1 & MT_TX_STAT_FIFO_VALID); - stat.success = !!(stat1 & MT_TX_STAT_FIFO_SUCCESS); - stat.aggr = !!(stat1 & MT_TX_STAT_FIFO_AGGR); - stat.ack_req = !!(stat1 & MT_TX_STAT_FIFO_ACKREQ); - stat.wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, stat1); - stat.rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, stat1); - - stat.retry = FIELD_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2); - stat.pktid = FIELD_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2); - - return stat; -} - -void mt76x0_send_tx_status(struct mt76x0_dev *dev, struct mt76_tx_status *stat, u8 *update) -{ - struct ieee80211_tx_info info = {}; - struct ieee80211_sta *sta = NULL; - struct mt76_wcid *wcid = NULL; - struct mt76_sta *msta = NULL; - - rcu_read_lock(); - if (stat->wcid < ARRAY_SIZE(dev->wcid)) - wcid = rcu_dereference(dev->wcid[stat->wcid]); - - if (wcid) { - void *priv; - priv = msta = container_of(wcid, struct mt76_sta, wcid); - sta = container_of(priv, struct ieee80211_sta, drv_priv); - } - - if (msta && stat->aggr) { - u32 stat_val, stat_cache; - - stat_val = stat->rate; - stat_val |= ((u32) stat->retry) << 16; - stat_cache = msta->status.rate; - stat_cache |= ((u32) msta->status.retry) << 16; - - if (*update == 0 && stat_val == stat_cache && - stat->wcid == msta->status.wcid && msta->n_frames < 32) { - msta->n_frames++; - goto out; - } - - mt76_mac_fill_tx_status(dev, &info, &msta->status, - msta->n_frames); - msta->status = *stat; - msta->n_frames = 1; - *update = 0; - } else { - mt76_mac_fill_tx_status(dev, &info, stat, 1); - *update = 1; - } - - spin_lock_bh(&dev->mac_lock); - ieee80211_tx_status_noskb(dev->mt76.hw, sta, &info); - spin_unlock_bh(&dev->mac_lock); -out: - rcu_read_unlock(); -} - void mt76x0_mac_set_protection(struct mt76x0_dev *dev, bool legacy_prot, int ht_mode) { @@ -355,8 +127,8 @@ void mt76x0_mac_work(struct work_struct *work) u32 span; u64 *stat_base; } spans[] = { - { MT_RX_STA_CNT0, 3, dev->stats.rx_stat }, - { MT_TX_STA_CNT0, 3, dev->stats.tx_stat }, + { MT_RX_STAT_0, 3, dev->stats.rx_stat }, + { MT_TX_STA_0, 3, dev->stats.tx_stat }, { MT_TX_AGG_STAT, 1, dev->stats.aggr_stat }, { MT_MPDU_DENSITY_CNT, 1, dev->stats.zero_len_del }, { MT_TX_AGG_CNT_BASE0, 8, &dev->stats.aggr_n[0] }, @@ -399,23 +171,6 @@ void mt76x0_mac_work(struct work_struct *work) ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mac_work, 10 * HZ); } -void -mt76x0_mac_wcid_setup(struct mt76x0_dev *dev, u8 idx, u8 vif_idx, u8 *mac) -{ - u8 zmac[ETH_ALEN] = {}; - u32 attr; - - attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | - FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); - - mt76_wr(dev, MT_WCID_ATTR(idx), attr); - - if (mac) - memcpy(zmac, mac, sizeof(zmac)); - - mt76x0_addr_wr(dev, MT_WCID_ADDR(idx), zmac); -} - void mt76x0_mac_set_ampdu_factor(struct mt76x0_dev *dev) { struct ieee80211_sta *sta; @@ -425,12 +180,12 @@ void mt76x0_mac_set_ampdu_factor(struct mt76x0_dev *dev) int i; rcu_read_lock(); - for (i = 0; i < ARRAY_SIZE(dev->wcid); i++) { - wcid = rcu_dereference(dev->wcid[i]); + for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid); i++) { + wcid = rcu_dereference(dev->mt76.wcid[i]); if (!wcid) continue; - msta = container_of(wcid, struct mt76_sta, wcid); + msta = container_of(wcid, struct mt76x02_sta, wcid); sta = container_of(msta, struct ieee80211_sta, drv_priv); min_factor = min(min_factor, sta->ht_cap.ampdu_factor); @@ -442,74 +197,7 @@ void mt76x0_mac_set_ampdu_factor(struct mt76x0_dev *dev) } static void -mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) -{ - u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); - - switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { - case MT_PHY_TYPE_OFDM: - if (idx >= 8) - idx = 0; - - if (status->band == NL80211_BAND_2GHZ) - idx += 4; - - status->rate_idx = idx; - return; - case MT_PHY_TYPE_CCK: - if (idx >= 8) { - idx -= 8; - status->enc_flags |= RX_ENC_FLAG_SHORTPRE; - } - - if (idx >= 4) - idx = 0; - - status->rate_idx = idx; - return; - case MT_PHY_TYPE_HT_GF: - status->enc_flags |= RX_ENC_FLAG_HT_GF; - /* fall through */ - case MT_PHY_TYPE_HT: - status->encoding = RX_ENC_HT; - status->rate_idx = idx; - break; - case MT_PHY_TYPE_VHT: - status->encoding = RX_ENC_VHT; - status->rate_idx = FIELD_GET(MT_RATE_INDEX_VHT_IDX, idx); - status->nss = FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1; - break; - default: - WARN_ON(1); - return; - } - - if (rate & MT_RXWI_RATE_LDPC) - status->enc_flags |= RX_ENC_FLAG_LDPC; - - if (rate & MT_RXWI_RATE_SGI) - status->enc_flags |= RX_ENC_FLAG_SHORT_GI; - - if (rate & MT_RXWI_RATE_STBC) - status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; - - switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { - case MT_PHY_BW_20: - break; - case MT_PHY_BW_40: - status->bw = RATE_INFO_BW_40; - break; - case MT_PHY_BW_80: - status->bw = RATE_INFO_BW_80; - break; - default: - WARN_ON(1); - break; - } -} - -static void -mt76x0_rx_monitor_beacon(struct mt76x0_dev *dev, struct mt76x0_rxwi *rxwi, +mt76x0_rx_monitor_beacon(struct mt76x0_dev *dev, struct mt76x02_rxwi *rxwi, u16 rate, int rssi) { dev->bcn_phy_mode = FIELD_GET(MT_RXWI_RATE_PHY, rate); @@ -526,13 +214,13 @@ mt76x0_rx_is_our_beacon(struct mt76x0_dev *dev, u8 *data) } u32 mt76x0_mac_process_rx(struct mt76x0_dev *dev, struct sk_buff *skb, - u8 *data, void *rxi) + void *rxi) { - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - struct mt76x0_rxwi *rxwi = rxi; + struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb; + struct mt76x02_rxwi *rxwi = rxi; u32 len, ctl = le32_to_cpu(rxwi->ctl); u16 rate = le16_to_cpu(rxwi->rate); - int rssi; + int rssi, pad_len = 0; len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl); if (WARN_ON(len < 10)) @@ -543,18 +231,24 @@ u32 mt76x0_mac_process_rx(struct mt76x0_dev *dev, struct sk_buff *skb, status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; } + if (rxwi->rxinfo & MT_RXINFO_L2PAD) + pad_len += 2; + + mt76x02_remove_hdr_pad(skb, pad_len); + + pskb_trim(skb, len); status->chains = BIT(0); rssi = mt76x0_phy_get_rssi(dev, rxwi); status->chain_signal[0] = status->signal = rssi; status->freq = dev->mt76.chandef.chan->center_freq; status->band = dev->mt76.chandef.chan->band; - mt76_mac_process_rate(status, rate); + mt76x02_mac_process_rate(status, rate); spin_lock_bh(&dev->con_mon_lock); - if (mt76x0_rx_is_our_beacon(dev, data)) { + if (mt76x0_rx_is_our_beacon(dev, skb->data)) { mt76x0_rx_monitor_beacon(dev, rxwi, rate, rssi); - } else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_U2M)) { + } else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_UNICAST)) { if (dev->avg_rssi == 0) dev->avg_rssi = rssi; else @@ -565,94 +259,3 @@ u32 mt76x0_mac_process_rx(struct mt76x0_dev *dev, struct sk_buff *skb, return len; } - -static enum mt76_cipher_type -mt76_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) -{ - memset(key_data, 0, 32); - if (!key) - return MT_CIPHER_NONE; - - if (key->keylen > 32) - return MT_CIPHER_NONE; - - memcpy(key_data, key->key, key->keylen); - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - return MT_CIPHER_WEP40; - case WLAN_CIPHER_SUITE_WEP104: - return MT_CIPHER_WEP104; - case WLAN_CIPHER_SUITE_TKIP: - return MT_CIPHER_TKIP; - case WLAN_CIPHER_SUITE_CCMP: - return MT_CIPHER_AES_CCMP; - default: - return MT_CIPHER_NONE; - } -} - -int mt76x0_mac_wcid_set_key(struct mt76x0_dev *dev, u8 idx, - struct ieee80211_key_conf *key) -{ - enum mt76_cipher_type cipher; - u8 key_data[32]; - u8 iv_data[8]; - u32 val; - - cipher = mt76_mac_get_key_info(key, key_data); - if (cipher == MT_CIPHER_NONE && key) - return -EINVAL; - - trace_mt76x0_set_key(&dev->mt76, idx); - - mt76_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); - - memset(iv_data, 0, sizeof(iv_data)); - if (key) { - iv_data[3] = key->keyidx << 6; - if (cipher >= MT_CIPHER_TKIP) { - /* Note: start with 1 to comply with spec, - * (see comment on common/cmm_wpa.c:4291). - */ - iv_data[0] |= 1; - iv_data[3] |= 0x20; - } - } - mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); - - val = mt76_rr(dev, MT_WCID_ATTR(idx)); - val &= ~MT_WCID_ATTR_PKEY_MODE & ~MT_WCID_ATTR_PKEY_MODE_EXT; - val |= FIELD_PREP(MT_WCID_ATTR_PKEY_MODE, cipher & 7) | - FIELD_PREP(MT_WCID_ATTR_PKEY_MODE_EXT, cipher >> 3); - val &= ~MT_WCID_ATTR_PAIRWISE; - val |= MT_WCID_ATTR_PAIRWISE * - !!(key && key->flags & IEEE80211_KEY_FLAG_PAIRWISE); - mt76_wr(dev, MT_WCID_ATTR(idx), val); - - return 0; -} - -int mt76x0_mac_shared_key_setup(struct mt76x0_dev *dev, u8 vif_idx, u8 key_idx, - struct ieee80211_key_conf *key) -{ - enum mt76_cipher_type cipher; - u8 key_data[32]; - u32 val; - - cipher = mt76_mac_get_key_info(key, key_data); - if (cipher == MT_CIPHER_NONE && key) - return -EINVAL; - - trace_mt76x0_set_shared_key(&dev->mt76, vif_idx, key_idx); - - mt76_wr_copy(dev, MT_SKEY(vif_idx, key_idx), - key_data, sizeof(key_data)); - - val = mt76_rr(dev, MT_SKEY_MODE(vif_idx)); - val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); - val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); - mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); - - return 0; -} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mac.h b/drivers/net/wireless/mediatek/mt76/mt76x0/mac.h index bea067b71c13..b887693a56b6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/mac.h @@ -15,140 +15,6 @@ #ifndef __MT76_MAC_H #define __MT76_MAC_H -/* Note: values in original "RSSI" and "SNR" fields are not actually what they - * are called for MT76X0U, names used by this driver are educated guesses - * (see vendor mac/ral_omac.c). - */ -struct mt76x0_rxwi { - __le32 rxinfo; - - __le32 ctl; - - __le16 tid_sn; - __le16 rate; - - s8 rssi[4]; - - __le32 bbp_rxinfo[4]; -} __packed __aligned(4); - -#define MT_RXINFO_BA BIT(0) -#define MT_RXINFO_DATA BIT(1) -#define MT_RXINFO_NULL BIT(2) -#define MT_RXINFO_FRAG BIT(3) -#define MT_RXINFO_U2M BIT(4) -#define MT_RXINFO_MULTICAST BIT(5) -#define MT_RXINFO_BROADCAST BIT(6) -#define MT_RXINFO_MYBSS BIT(7) -#define MT_RXINFO_CRCERR BIT(8) -#define MT_RXINFO_ICVERR BIT(9) -#define MT_RXINFO_MICERR BIT(10) -#define MT_RXINFO_AMSDU BIT(11) -#define MT_RXINFO_HTC BIT(12) -#define MT_RXINFO_RSSI BIT(13) -#define MT_RXINFO_L2PAD BIT(14) -#define MT_RXINFO_AMPDU BIT(15) -#define MT_RXINFO_DECRYPT BIT(16) -#define MT_RXINFO_BSSIDX3 BIT(17) -#define MT_RXINFO_WAPI_KEY BIT(18) -#define MT_RXINFO_PN_LEN GENMASK(21, 19) -#define MT_RXINFO_SW_PKT_80211 BIT(22) -#define MT_RXINFO_TCP_SUM_BYPASS BIT(28) -#define MT_RXINFO_IP_SUM_BYPASS BIT(29) -#define MT_RXINFO_TCP_SUM_ERR BIT(30) -#define MT_RXINFO_IP_SUM_ERR BIT(31) - -#define MT_RXWI_CTL_WCID GENMASK(7, 0) -#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) -#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) -#define MT_RXWI_CTL_UDF GENMASK(15, 13) -#define MT_RXWI_CTL_MPDU_LEN GENMASK(27, 16) -#define MT_RXWI_CTL_TID GENMASK(31, 28) - -#define MT_RXWI_FRAG GENMASK(3, 0) -#define MT_RXWI_SN GENMASK(15, 4) - -#define MT_RXWI_RATE_INDEX GENMASK(5, 0) -#define MT_RXWI_RATE_LDPC BIT(6) -#define MT_RXWI_RATE_BW GENMASK(8, 7) -#define MT_RXWI_RATE_SGI BIT(9) -#define MT_RXWI_RATE_STBC BIT(10) -#define MT_RXWI_RATE_LDPC_ETXBF BIT(11) -#define MT_RXWI_RATE_SND BIT(12) -#define MT_RXWI_RATE_PHY GENMASK(15, 13) - -#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0) -#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4) - -#define MT_RXWI_GAIN_RSSI_VAL GENMASK(5, 0) -#define MT_RXWI_GAIN_RSSI_LNA_ID GENMASK(7, 6) -#define MT_RXWI_ANT_AUX_LNA BIT(7) - -#define MT_RXWI_EANT_ENC_ANT_ID GENMASK(7, 0) - -enum mt76_phy_bandwidth { - MT_PHY_BW_20, - MT_PHY_BW_40, - MT_PHY_BW_80, -}; - -struct mt76_txwi { - __le16 flags; - __le16 rate_ctl; - u8 ack_ctl; - u8 wcid; - __le16 len_ctl; - __le32 iv; - __le32 eiv; - u8 aid; - u8 txstream; - u8 ctl2; - u8 pktid; -} __packed __aligned(4); - -#define MT_TXWI_FLAGS_FRAG BIT(0) -#define MT_TXWI_FLAGS_MMPS BIT(1) -#define MT_TXWI_FLAGS_CFACK BIT(2) -#define MT_TXWI_FLAGS_TS BIT(3) -#define MT_TXWI_FLAGS_AMPDU BIT(4) -#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) -#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) -#define MT_TXWI_FLAGS_CWMIN GENMASK(12, 10) -#define MT_TXWI_FLAGS_NO_RATE_FALLBACK BIT(13) -#define MT_TXWI_FLAGS_TX_RPT BIT(14) -#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) - -#define MT_TXWI_RATE_MCS GENMASK(6, 0) -#define MT_TXWI_RATE_BW BIT(7) -#define MT_TXWI_RATE_SGI BIT(8) -#define MT_TXWI_RATE_STBC GENMASK(10, 9) -#define MT_TXWI_RATE_PHY_MODE GENMASK(15, 14) - -#define MT_TXWI_ACK_CTL_REQ BIT(0) -#define MT_TXWI_ACK_CTL_NSEQ BIT(1) -#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) - -#define MT_TXWI_LEN_BYTE_CNT GENMASK(11, 0) - -#define MT_TXWI_CTL_TX_POWER_ADJ GENMASK(3, 0) -#define MT_TXWI_CTL_CHAN_CHECK_PKT BIT(4) -#define MT_TXWI_CTL_PIFS_REV BIT(6) - -#define MT_TXWI_PKTID_PROBE BIT(7) - u32 mt76x0_mac_process_rx(struct mt76x0_dev *dev, struct sk_buff *skb, - u8 *data, void *rxi); -int mt76x0_mac_wcid_set_key(struct mt76x0_dev *dev, u8 idx, - struct ieee80211_key_conf *key); -void mt76x0_mac_wcid_set_rate(struct mt76x0_dev *dev, struct mt76_wcid *wcid, - const struct ieee80211_tx_rate *rate); - -int mt76x0_mac_shared_key_setup(struct mt76x0_dev *dev, u8 vif_idx, u8 key_idx, - struct ieee80211_key_conf *key); -u16 mt76x0_mac_tx_rate_val(struct mt76x0_dev *dev, - const struct ieee80211_tx_rate *rate, u8 *nss_val); -struct mt76_tx_status -mt76x0_mac_fetch_tx_status(struct mt76x0_dev *dev); -void mt76x0_send_tx_status(struct mt76x0_dev *dev, struct mt76_tx_status *stat, u8 *update); - + void *rxi); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c index cf6ffb1ba4a2..c84e00abfac9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c @@ -15,6 +15,7 @@ #include "mt76x0.h" #include "mac.h" +#include "../mt76x02_util.h" #include <linux/etherdevice.h> static int mt76x0_start(struct ieee80211_hw *hw) @@ -22,7 +23,7 @@ static int mt76x0_start(struct ieee80211_hw *hw) struct mt76x0_dev *dev = hw->priv; int ret; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); ret = mt76x0_mac_start(dev); if (ret) @@ -32,8 +33,11 @@ static int mt76x0_start(struct ieee80211_hw *hw) MT_CALIBRATE_INTERVAL); ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work, MT_CALIBRATE_INTERVAL); + + set_bit(MT76_STATE_RUNNING, &dev->mt76.state); + out: - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return ret; } @@ -41,45 +45,12 @@ static void mt76x0_stop(struct ieee80211_hw *hw) { struct mt76x0_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); - cancel_delayed_work_sync(&dev->cal_work); - cancel_delayed_work_sync(&dev->mac_work); + clear_bit(MT76_STATE_RUNNING, &dev->mt76.state); mt76x0_mac_stop(dev); - mutex_unlock(&dev->mutex); -} - - -static int mt76x0_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mt76x0_dev *dev = hw->priv; - struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; - unsigned int idx; - - idx = ffs(~dev->vif_mask); - if (!idx || idx > 8) - return -ENOSPC; - - idx--; - dev->vif_mask |= BIT(idx); - - mvif->idx = idx; - mvif->group_wcid.idx = GROUP_WCID(idx); - mvif->group_wcid.hw_key_idx = -1; - - return 0; -} - -static void mt76x0_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mt76x0_dev *dev = hw->priv; - struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; - unsigned int wcid = mvif->group_wcid.idx; - - dev->wcid_mask[wcid / BITS_PER_LONG] &= ~BIT(wcid % BITS_PER_LONG); + mutex_unlock(&dev->mt76.mutex); } static int mt76x0_config(struct ieee80211_hw *hw, u32 changed) @@ -87,16 +58,7 @@ static int mt76x0_config(struct ieee80211_hw *hw, u32 changed) struct mt76x0_dev *dev = hw->priv; int ret = 0; - mutex_lock(&dev->mutex); - - if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) - dev->rxfilter |= MT_RX_FILTR_CFG_PROMISC; - else - dev->rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; - - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); - } + mutex_lock(&dev->mt76.mutex); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { ieee80211_stop_queues(hw); @@ -104,42 +66,16 @@ static int mt76x0_config(struct ieee80211_hw *hw, u32 changed) ieee80211_wake_queues(hw); } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return ret; } static void -mt76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) +mt76x0_addr_wr(struct mt76x0_dev *dev, const u32 offset, const u8 *addr) { - struct mt76x0_dev *dev = hw->priv; - u32 flags = 0; - -#define MT76_FILTER(_flag, _hw) do { \ - flags |= *total_flags & FIF_##_flag; \ - dev->rxfilter &= ~(_hw); \ - dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ - } while (0) - - mutex_lock(&dev->mutex); - - dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; - - MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); - MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); - MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | - MT_RX_FILTR_CFG_CTS | - MT_RX_FILTR_CFG_CFEND | - MT_RX_FILTR_CFG_CFACK | - MT_RX_FILTR_CFG_BA | - MT_RX_FILTR_CFG_CTRL_RSV); - MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); - - *total_flags = flags; - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); - - mutex_unlock(&dev->mutex); + mt76_wr(dev, offset, get_unaligned_le32(addr)); + mt76_wr(dev, offset + 4, addr[4] | addr[5] << 8); } static void @@ -148,7 +84,7 @@ mt76x0_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt76x0_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); if (changed & BSS_CHANGED_ASSOC) mt76x0_phy_con_cal_onoff(dev, info); @@ -166,8 +102,8 @@ mt76x0_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (changed & BSS_CHANGED_BASIC_RATES) { mt76_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates); - mt76_wr(dev, MT_HT_FBK_CFG0, 0x65432100); - mt76_wr(dev, MT_HT_FBK_CFG1, 0xedcba980); + mt76_wr(dev, MT_VHT_HT_FBK_CFG0, 0x65432100); + mt76_wr(dev, MT_VHT_HT_FBK_CFG1, 0xedcba980); mt76_wr(dev, MT_LG_FBK_CFG0, 0xedcba988); mt76_wr(dev, MT_LG_FBK_CFG1, 0x00002100); } @@ -192,63 +128,7 @@ mt76x0_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (changed & BSS_CHANGED_ASSOC) mt76x0_phy_recalibrate_after_assoc(dev); - mutex_unlock(&dev->mutex); -} - -static int -mt76x0_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt76x0_dev *dev = hw->priv; - struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; - struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; - int ret = 0; - int idx = 0; - - mutex_lock(&dev->mutex); - - idx = mt76_wcid_alloc(dev->wcid_mask, ARRAY_SIZE(dev->wcid)); - if (idx < 0) { - ret = -ENOSPC; - goto out; - } - - msta->wcid.idx = idx; - msta->wcid.hw_key_idx = -1; - mt76x0_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); - mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx)); - rcu_assign_pointer(dev->wcid[idx], &msta->wcid); - mt76x0_mac_set_ampdu_factor(dev); - -out: - mutex_unlock(&dev->mutex); - - return ret; -} - -static int -mt76x0_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt76x0_dev *dev = hw->priv; - struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; - int idx = msta->wcid.idx; - - mutex_lock(&dev->mutex); - rcu_assign_pointer(dev->wcid[idx], NULL); - mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx)); - dev->wcid_mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG); - mt76x0_mac_wcid_setup(dev, idx, 0, NULL); - mt76x0_mac_set_ampdu_factor(dev); - mutex_unlock(&dev->mutex); - - return 0; -} - -static void -mt76x0_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, struct ieee80211_sta *sta) -{ + mutex_unlock(&dev->mt76.mutex); } static void @@ -276,41 +156,6 @@ mt76x0_sw_scan_complete(struct ieee80211_hw *hw, MT_CALIBRATE_INTERVAL); } -static int -mt76x0_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct mt76x0_dev *dev = hw->priv; - struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; - struct mt76_sta *msta = sta ? (struct mt76_sta *) sta->drv_priv : NULL; - struct mt76_wcid *wcid = msta ? &msta->wcid : &mvif->group_wcid; - int idx = key->keyidx; - int ret; - - if (cmd == SET_KEY) { - key->hw_key_idx = wcid->idx; - wcid->hw_key_idx = idx; - } else { - if (idx == wcid->hw_key_idx) - wcid->hw_key_idx = -1; - - key = NULL; - } - - if (!msta) { - if (key || wcid->hw_key_idx == idx) { - ret = mt76x0_mac_wcid_set_key(dev, wcid->idx, key); - if (ret) - return ret; - } - - return mt76x0_mac_shared_key_setup(dev, mvif->idx, idx, key); - } - - return mt76x0_mac_wcid_set_key(dev, msta->wcid.idx, key); -} - static int mt76x0_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct mt76x0_dev *dev = hw->priv; @@ -320,84 +165,23 @@ static int mt76x0_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return 0; } -static int -mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_ampdu_params *params) -{ - struct mt76x0_dev *dev = hw->priv; - struct ieee80211_sta *sta = params->sta; - enum ieee80211_ampdu_mlme_action action = params->action; - u16 tid = params->tid; - u16 *ssn = ¶ms->ssn; - struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; - - WARN_ON(msta->wcid.idx > N_WCIDS); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); - break; - case IEEE80211_AMPDU_RX_STOP: - mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]); - break; - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - break; - case IEEE80211_AMPDU_TX_START: - msta->agg_ssn[tid] = *ssn << 4; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - } - - return 0; -} - -static void -mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt76x0_dev *dev = hw->priv; - struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; - struct ieee80211_sta_rates *rates; - struct ieee80211_tx_rate rate = {}; - - rcu_read_lock(); - rates = rcu_dereference(sta->rates); - - if (!rates) - goto out; - - rate.idx = rates->rate[0].idx; - rate.flags = rates->rate[0].flags; - mt76x0_mac_wcid_set_rate(dev, &msta->wcid, &rate); - -out: - rcu_read_unlock(); -} - const struct ieee80211_ops mt76x0_ops = { .tx = mt76x0_tx, .start = mt76x0_start, .stop = mt76x0_stop, - .add_interface = mt76x0_add_interface, - .remove_interface = mt76x0_remove_interface, + .add_interface = mt76x02_add_interface, + .remove_interface = mt76x02_remove_interface, .config = mt76x0_config, - .configure_filter = mt76_configure_filter, + .configure_filter = mt76x02_configure_filter, .bss_info_changed = mt76x0_bss_info_changed, - .sta_add = mt76x0_sta_add, - .sta_remove = mt76x0_sta_remove, - .sta_notify = mt76x0_sta_notify, - .set_key = mt76x0_set_key, - .conf_tx = mt76x0_conf_tx, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + .set_key = mt76x02_set_key, + .conf_tx = mt76x02_conf_tx, .sw_scan_start = mt76x0_sw_scan, .sw_scan_complete = mt76x0_sw_scan_complete, - .ampdu_action = mt76_ampdu_action, - .sta_rate_tbl_update = mt76_sta_rate_tbl_update, + .ampdu_action = mt76x02_ampdu_action, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, .set_rts_threshold = mt76x0_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.c deleted file mode 100644 index 8affacbab90a..000000000000 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.c +++ /dev/null @@ -1,656 +0,0 @@ -/* - * (c) Copyright 2002-2010, Ralink Technology, Inc. - * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> - * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> - * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * 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. - */ - -#include <linux/kernel.h> -#include <linux/firmware.h> -#include <linux/delay.h> -#include <linux/usb.h> -#include <linux/skbuff.h> - -#include "mt76x0.h" -#include "dma.h" -#include "mcu.h" -#include "usb.h" -#include "trace.h" - -#define MCU_FW_URB_MAX_PAYLOAD 0x38f8 -#define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12) -#define MCU_RESP_URB_SIZE 1024 - -static inline int firmware_running(struct mt76x0_dev *dev) -{ - return mt76_rr(dev, MT_MCU_COM_REG0) == 1; -} - -static inline void skb_put_le32(struct sk_buff *skb, u32 val) -{ - put_unaligned_le32(val, skb_put(skb, 4)); -} - -static inline void mt76x0_dma_skb_wrap_cmd(struct sk_buff *skb, - u8 seq, enum mcu_cmd cmd) -{ - WARN_ON(mt76x0_dma_skb_wrap(skb, CPU_TX_PORT, DMA_COMMAND, - FIELD_PREP(MT_TXD_CMD_SEQ, seq) | - FIELD_PREP(MT_TXD_CMD_TYPE, cmd))); -} - -static inline void trace_mt76x0_mcu_msg_send_cs(struct mt76_dev *dev, - struct sk_buff *skb, bool need_resp) -{ - u32 i, csum = 0; - - for (i = 0; i < skb->len / 4; i++) - csum ^= get_unaligned_le32(skb->data + i * 4); - - trace_mt76x0_mcu_msg_send(dev, skb, csum, need_resp); -} - -static struct sk_buff * -mt76x0_mcu_msg_alloc(struct mt76x0_dev *dev, const void *data, int len) -{ - struct sk_buff *skb; - - WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */ - - skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL); - if (skb) { - skb_reserve(skb, MT_DMA_HDR_LEN); - memcpy(skb_put(skb, len), data, len); - } - return skb; -} - -static void mt76x0_read_resp_regs(struct mt76x0_dev *dev, int len) -{ - int i; - int n = dev->mcu.reg_pairs_len; - u8 *buf = dev->mcu.resp.buf; - - buf += 4; - len -= 8; - - if (dev->mcu.burst_read) { - u32 reg = dev->mcu.reg_pairs[0].reg - dev->mcu.reg_base; - - WARN_ON_ONCE(len/4 != n); - for (i = 0; i < n; i++) { - u32 val = get_unaligned_le32(buf + 4*i); - - dev->mcu.reg_pairs[i].reg = reg++; - dev->mcu.reg_pairs[i].value = val; - } - } else { - WARN_ON_ONCE(len/8 != n); - for (i = 0; i < n; i++) { - u32 reg = get_unaligned_le32(buf + 8*i) - dev->mcu.reg_base; - u32 val = get_unaligned_le32(buf + 8*i + 4); - - WARN_ON_ONCE(dev->mcu.reg_pairs[i].reg != reg); - dev->mcu.reg_pairs[i].value = val; - } - } -} - -static int mt76x0_mcu_wait_resp(struct mt76x0_dev *dev, u8 seq) -{ - struct urb *urb = dev->mcu.resp.urb; - u32 rxfce; - int urb_status, ret, try = 5; - - while (try--) { - if (!wait_for_completion_timeout(&dev->mcu.resp_cmpl, - msecs_to_jiffies(300))) { - dev_warn(dev->mt76.dev, "Warning: %s retrying\n", __func__); - continue; - } - - /* Make copies of important data before reusing the urb */ - rxfce = get_unaligned_le32(dev->mcu.resp.buf); - urb_status = urb->status * mt76x0_urb_has_error(urb); - - if (urb_status == 0 && dev->mcu.reg_pairs) - mt76x0_read_resp_regs(dev, urb->actual_length); - - ret = mt76x0_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP, - &dev->mcu.resp, GFP_KERNEL, - mt76x0_complete_urb, - &dev->mcu.resp_cmpl); - if (ret) - return ret; - - if (urb_status) - dev_err(dev->mt76.dev, "Error: MCU resp urb failed:%d\n", - urb_status); - - if (FIELD_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce) == seq && - FIELD_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce) == CMD_DONE) - return 0; - - dev_err(dev->mt76.dev, "Error: MCU resp evt:%lx seq:%hhx-%lx!\n", - FIELD_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce), - seq, FIELD_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce)); - } - - dev_err(dev->mt76.dev, "Error: %s timed out\n", __func__); - return -ETIMEDOUT; -} - -static int -__mt76x0_mcu_msg_send(struct mt76x0_dev *dev, struct sk_buff *skb, - enum mcu_cmd cmd, bool wait_resp) -{ - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - unsigned cmd_pipe = usb_sndbulkpipe(usb_dev, - dev->out_ep[MT_EP_OUT_INBAND_CMD]); - int sent, ret; - u8 seq = 0; - - if (wait_resp) - while (!seq) - seq = ++dev->mcu.msg_seq & 0xf; - - mt76x0_dma_skb_wrap_cmd(skb, seq, cmd); - - if (dev->mcu.resp_cmpl.done) - dev_err(dev->mt76.dev, "Error: MCU response pre-completed!\n"); - - trace_mt76x0_mcu_msg_send_cs(&dev->mt76, skb, wait_resp); - trace_mt76x0_submit_urb_sync(&dev->mt76, cmd_pipe, skb->len); - - ret = usb_bulk_msg(usb_dev, cmd_pipe, skb->data, skb->len, &sent, 500); - if (ret) { - dev_err(dev->mt76.dev, "Error: send MCU cmd failed:%d\n", ret); - goto out; - } - if (sent != skb->len) - dev_err(dev->mt76.dev, "Error: %s sent != skb->len\n", __func__); - - if (wait_resp) - ret = mt76x0_mcu_wait_resp(dev, seq); - -out: - return ret; -} - -static int -mt76x0_mcu_msg_send(struct mt76x0_dev *dev, struct sk_buff *skb, - enum mcu_cmd cmd, bool wait_resp) -{ - int ret; - - if (test_bit(MT76_REMOVED, &dev->mt76.state)) - return 0; - - mutex_lock(&dev->mcu.mutex); - ret = __mt76x0_mcu_msg_send(dev, skb, cmd, wait_resp); - mutex_unlock(&dev->mcu.mutex); - - consume_skb(skb); - - return ret; -} - -int mt76x0_mcu_function_select(struct mt76x0_dev *dev, - enum mcu_function func, u32 val) -{ - struct sk_buff *skb; - struct { - __le32 id; - __le32 value; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(func), - .value = cpu_to_le32(val), - }; - - skb = mt76x0_mcu_msg_alloc(dev, &msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76x0_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5); -} - -int -mt76x0_mcu_calibrate(struct mt76x0_dev *dev, enum mcu_calibrate cal, u32 val) -{ - struct sk_buff *skb; - struct { - __le32 id; - __le32 value; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(cal), - .value = cpu_to_le32(val), - }; - - skb = mt76x0_mcu_msg_alloc(dev, &msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76x0_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true); -} - -int mt76x0_write_reg_pairs(struct mt76x0_dev *dev, u32 base, - const struct mt76_reg_pair *data, int n) -{ - const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 8; - struct sk_buff *skb; - int cnt, i, ret; - - if (!n) - return 0; - - cnt = min(max_vals_per_cmd, n); - - skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); - if (!skb) - return -ENOMEM; - skb_reserve(skb, MT_DMA_HDR_LEN); - - for (i = 0; i < cnt; i++) { - skb_put_le32(skb, base + data[i].reg); - skb_put_le32(skb, data[i].value); - } - - ret = mt76x0_mcu_msg_send(dev, skb, CMD_RANDOM_WRITE, cnt == n); - if (ret) - return ret; - - return mt76x0_write_reg_pairs(dev, base, data + cnt, n - cnt); -} - -int mt76x0_read_reg_pairs(struct mt76x0_dev *dev, u32 base, - struct mt76_reg_pair *data, int n) -{ - const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 8; - struct sk_buff *skb; - int cnt, i, ret; - - if (!n) - return 0; - - cnt = min(max_vals_per_cmd, n); - if (cnt != n) - return -EINVAL; - - skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); - if (!skb) - return -ENOMEM; - skb_reserve(skb, MT_DMA_HDR_LEN); - - for (i = 0; i < cnt; i++) { - skb_put_le32(skb, base + data[i].reg); - skb_put_le32(skb, data[i].value); - } - - mutex_lock(&dev->mcu.mutex); - - dev->mcu.reg_pairs = data; - dev->mcu.reg_pairs_len = n; - dev->mcu.reg_base = base; - dev->mcu.burst_read = false; - - ret = __mt76x0_mcu_msg_send(dev, skb, CMD_RANDOM_READ, true); - - dev->mcu.reg_pairs = NULL; - - mutex_unlock(&dev->mcu.mutex); - - consume_skb(skb); - - return ret; - -} - -int mt76x0_burst_write_regs(struct mt76x0_dev *dev, u32 offset, - const u32 *data, int n) -{ - const int max_regs_per_cmd = INBAND_PACKET_MAX_LEN / 4 - 1; - struct sk_buff *skb; - int cnt, i, ret; - - if (!n) - return 0; - - cnt = min(max_regs_per_cmd, n); - - skb = alloc_skb(cnt * 4 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); - if (!skb) - return -ENOMEM; - skb_reserve(skb, MT_DMA_HDR_LEN); - - skb_put_le32(skb, MT_MCU_MEMMAP_WLAN + offset); - for (i = 0; i < cnt; i++) - skb_put_le32(skb, data[i]); - - ret = mt76x0_mcu_msg_send(dev, skb, CMD_BURST_WRITE, cnt == n); - if (ret) - return ret; - - return mt76x0_burst_write_regs(dev, offset + cnt * 4, - data + cnt, n - cnt); -} - -#if 0 -static int mt76x0_burst_read_regs(struct mt76x0_dev *dev, u32 base, - struct mt76_reg_pair *data, int n) -{ - const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 4 - 1; - struct sk_buff *skb; - int cnt, ret; - - if (!n) - return 0; - - cnt = min(max_vals_per_cmd, n); - if (cnt != n) - return -EINVAL; - - skb = alloc_skb(cnt * 4 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); - if (!skb) - return -ENOMEM; - skb_reserve(skb, MT_DMA_HDR_LEN); - - skb_put_le32(skb, base + data[0].reg); - skb_put_le32(skb, n); - - mutex_lock(&dev->mcu.mutex); - - dev->mcu.reg_pairs = data; - dev->mcu.reg_pairs_len = n; - dev->mcu.reg_base = base; - dev->mcu.burst_read = true; - - ret = __mt76x0_mcu_msg_send(dev, skb, CMD_BURST_READ, true); - - dev->mcu.reg_pairs = NULL; - - mutex_unlock(&dev->mcu.mutex); - - consume_skb(skb); - - return ret; -} -#endif - -struct mt76_fw_header { - __le32 ilm_len; - __le32 dlm_len; - __le16 build_ver; - __le16 fw_ver; - u8 pad[4]; - char build_time[16]; -}; - -struct mt76_fw { - struct mt76_fw_header hdr; - u8 ivb[MT_MCU_IVB_SIZE]; - u8 ilm[]; -}; - -static int __mt76x0_dma_fw(struct mt76x0_dev *dev, - const struct mt76x0_dma_buf *dma_buf, - const void *data, u32 len, u32 dst_addr) -{ - DECLARE_COMPLETION_ONSTACK(cmpl); - struct mt76x0_dma_buf buf = *dma_buf; /* we need to fake length */ - __le32 reg; - u32 val; - int ret; - - reg = cpu_to_le32(FIELD_PREP(MT_TXD_INFO_TYPE, DMA_COMMAND) | - FIELD_PREP(MT_TXD_INFO_D_PORT, CPU_TX_PORT) | - FIELD_PREP(MT_TXD_INFO_LEN, len)); - memcpy(buf.buf, ®, sizeof(reg)); - memcpy(buf.buf + sizeof(reg), data, len); - memset(buf.buf + sizeof(reg) + len, 0, 8); - - ret = mt76x0_vendor_single_wr(dev, MT_VEND_WRITE_FCE, - MT_FCE_DMA_ADDR, dst_addr); - if (ret) - return ret; - len = roundup(len, 4); - ret = mt76x0_vendor_single_wr(dev, MT_VEND_WRITE_FCE, - MT_FCE_DMA_LEN, len << 16); - if (ret) - return ret; - - buf.len = MT_DMA_HDR_LEN + len + 4; - ret = mt76x0_usb_submit_buf(dev, USB_DIR_OUT, MT_EP_OUT_INBAND_CMD, - &buf, GFP_KERNEL, - mt76x0_complete_urb, &cmpl); - if (ret) - return ret; - - if (!wait_for_completion_timeout(&cmpl, msecs_to_jiffies(1000))) { - dev_err(dev->mt76.dev, "Error: firmware upload timed out\n"); - usb_kill_urb(buf.urb); - return -ETIMEDOUT; - } - if (mt76x0_urb_has_error(buf.urb)) { - dev_err(dev->mt76.dev, "Error: firmware upload urb failed:%d\n", - buf.urb->status); - return buf.urb->status; - } - - val = mt76_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); - val++; - mt76_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); - - msleep(5); - - return 0; -} - -static int -mt76x0_dma_fw(struct mt76x0_dev *dev, struct mt76x0_dma_buf *dma_buf, - const void *data, int len, u32 dst_addr) -{ - int n, ret; - - if (len == 0) - return 0; - - n = min(MCU_FW_URB_MAX_PAYLOAD, len); - ret = __mt76x0_dma_fw(dev, dma_buf, data, n, dst_addr); - if (ret) - return ret; - -#if 0 - if (!mt76_poll_msec(dev, MT_MCU_COM_REG1, BIT(31), BIT(31), 500)) - return -ETIMEDOUT; -#endif - - return mt76x0_dma_fw(dev, dma_buf, data + n, len - n, dst_addr + n); -} - -static int -mt76x0_upload_firmware(struct mt76x0_dev *dev, const struct mt76_fw *fw) -{ - struct mt76x0_dma_buf dma_buf; - void *ivb; - u32 ilm_len, dlm_len; - int i, ret; - - ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL); - if (!ivb) - return -ENOMEM; - if (mt76x0_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) { - ret = -ENOMEM; - goto error; - } - - ilm_len = le32_to_cpu(fw->hdr.ilm_len) - sizeof(fw->ivb); - dev_dbg(dev->mt76.dev, "loading FW - ILM %u + IVB %zu\n", - ilm_len, sizeof(fw->ivb)); - ret = mt76x0_dma_fw(dev, &dma_buf, fw->ilm, ilm_len, sizeof(fw->ivb)); - if (ret) - goto error; - - dlm_len = le32_to_cpu(fw->hdr.dlm_len); - dev_dbg(dev->mt76.dev, "loading FW - DLM %u\n", dlm_len); - ret = mt76x0_dma_fw(dev, &dma_buf, fw->ilm + ilm_len, - dlm_len, MT_MCU_DLM_OFFSET); - if (ret) - goto error; - - ret = mt76x0_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, - 0x12, 0, ivb, sizeof(fw->ivb)); - if (ret < 0) - goto error; - ret = 0; - - for (i = 100; i && !firmware_running(dev); i--) - msleep(10); - if (!i) { - ret = -ETIMEDOUT; - goto error; - } - - dev_dbg(dev->mt76.dev, "Firmware running!\n"); -error: - kfree(ivb); - mt76x0_usb_free_buf(dev, &dma_buf); - - return ret; -} - -static int mt76x0_load_firmware(struct mt76x0_dev *dev) -{ - const struct firmware *fw; - const struct mt76_fw_header *hdr; - int len, ret; - u32 val; - - mt76_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | - MT_USB_DMA_CFG_TX_BULK_EN)); - - if (firmware_running(dev)) - return 0; - - ret = request_firmware(&fw, MT7610_FIRMWARE, dev->mt76.dev); - if (ret) - return ret; - - if (!fw || !fw->data || fw->size < sizeof(*hdr)) - goto err_inv_fw; - - hdr = (const struct mt76_fw_header *) fw->data; - - if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) - goto err_inv_fw; - - len = sizeof(*hdr); - len += le32_to_cpu(hdr->ilm_len); - len += le32_to_cpu(hdr->dlm_len); - - if (fw->size != len) - goto err_inv_fw; - - val = le16_to_cpu(hdr->fw_ver); - dev_dbg(dev->mt76.dev, - "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", - (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, - le16_to_cpu(hdr->build_ver), hdr->build_time); - - len = le32_to_cpu(hdr->ilm_len); - - mt76_wr(dev, 0x1004, 0x2c); - - mt76_set(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | - MT_USB_DMA_CFG_TX_BULK_EN) | - FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20)); - mt76x0_vendor_reset(dev); - msleep(5); -/* - mt76x0_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | - MT_PBF_CFG_TX1Q_EN | - MT_PBF_CFG_TX2Q_EN | - MT_PBF_CFG_TX3Q_EN)); -*/ - - mt76_wr(dev, MT_FCE_PSE_CTRL, 1); - - /* FCE tx_fs_base_ptr */ - mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); - /* FCE tx_fs_max_cnt */ - mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); - /* FCE pdma enable */ - mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); - /* FCE skip_fs_en */ - mt76_wr(dev, MT_FCE_SKIP_FS, 3); - - val = mt76_rr(dev, MT_USB_DMA_CFG); - val |= MT_USB_DMA_CFG_TX_WL_DROP; - mt76_wr(dev, MT_USB_DMA_CFG, val); - val &= ~MT_USB_DMA_CFG_TX_WL_DROP; - mt76_wr(dev, MT_USB_DMA_CFG, val); - - ret = mt76x0_upload_firmware(dev, (const struct mt76_fw *)fw->data); - release_firmware(fw); - - mt76_wr(dev, MT_FCE_PSE_CTRL, 1); - - return ret; - -err_inv_fw: - dev_err(dev->mt76.dev, "Invalid firmware image\n"); - release_firmware(fw); - return -ENOENT; -} - -int mt76x0_mcu_init(struct mt76x0_dev *dev) -{ - int ret; - - mutex_init(&dev->mcu.mutex); - - ret = mt76x0_load_firmware(dev); - if (ret) - return ret; - - set_bit(MT76_STATE_MCU_RUNNING, &dev->mt76.state); - - return 0; -} - -int mt76x0_mcu_cmd_init(struct mt76x0_dev *dev) -{ - int ret; - - ret = mt76x0_mcu_function_select(dev, Q_SELECT, 1); - if (ret) - return ret; - - init_completion(&dev->mcu.resp_cmpl); - if (mt76x0_usb_alloc_buf(dev, MCU_RESP_URB_SIZE, &dev->mcu.resp)) { - mt76x0_usb_free_buf(dev, &dev->mcu.resp); - return -ENOMEM; - } - - ret = mt76x0_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP, - &dev->mcu.resp, GFP_KERNEL, - mt76x0_complete_urb, &dev->mcu.resp_cmpl); - if (ret) { - mt76x0_usb_free_buf(dev, &dev->mcu.resp); - return ret; - } - - return 0; -} - -void mt76x0_mcu_cmd_deinit(struct mt76x0_dev *dev) -{ - usb_kill_urb(dev->mcu.resp.urb); - mt76x0_usb_free_buf(dev, &dev->mcu.resp); -} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h index 8c2f77f4c3f5..f2a87d283e09 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h @@ -15,65 +15,18 @@ #ifndef __MT76X0U_MCU_H #define __MT76X0U_MCU_H -struct mt76x0_dev; +#include "../mt76x02_mcu.h" -/* Register definitions */ -#define MT_MCU_RESET_CTL 0x070C -#define MT_MCU_INT_LEVEL 0x0718 -#define MT_MCU_COM_REG0 0x0730 -#define MT_MCU_COM_REG1 0x0734 -#define MT_MCU_COM_REG2 0x0738 -#define MT_MCU_COM_REG3 0x073C +struct mt76x0_dev; #define MT_MCU_IVB_SIZE 0x40 #define MT_MCU_DLM_OFFSET 0x80000 -#define MT_MCU_MEMMAP_WLAN 0x00410000 /* We use same space for BBP as for MAC regs * #define MT_MCU_MEMMAP_BBP 0x40000000 */ #define MT_MCU_MEMMAP_RF 0x80000000 -#define INBAND_PACKET_MAX_LEN 192 - -enum mcu_cmd { - CMD_FUN_SET_OP = 1, - CMD_LOAD_CR = 2, - CMD_INIT_GAIN_OP = 3, - CMD_DYNC_VGA_OP = 6, - CMD_TDLS_CH_SW = 7, - CMD_BURST_WRITE = 8, - CMD_READ_MODIFY_WRITE = 9, - CMD_RANDOM_READ = 10, - CMD_BURST_READ = 11, - CMD_RANDOM_WRITE = 12, - CMD_LED_MODE_OP = 16, - CMD_POWER_SAVING_OP = 20, - CMD_WOW_CONFIG = 21, - CMD_WOW_QUERY = 22, - CMD_WOW_FEATURE = 24, - CMD_CARRIER_DETECT_OP = 28, - CMD_RADOR_DETECT_OP = 29, - CMD_SWITCH_CHANNEL_OP = 30, - CMD_CALIBRATION_OP = 31, - CMD_BEACON_OP = 32, - CMD_ANTENNA_OP = 33, -}; - -enum mcu_function { - Q_SELECT = 1, - BW_SETTING = 2, - ATOMIC_TSSI_SETTING = 5, -}; - -enum mcu_power_mode { - RADIO_OFF = 0x30, - RADIO_ON = 0x31, - RADIO_OFF_AUTO_WAKEUP = 0x32, - RADIO_OFF_ADVANCE = 0x33, - RADIO_ON_ADVANCE = 0x34, -}; - enum mcu_calibrate { MCU_CAL_R = 1, MCU_CAL_RXDCOC, @@ -88,14 +41,4 @@ enum mcu_calibrate { MCU_CAL_TX_GROUP_DELAY, }; -int mt76x0_mcu_init(struct mt76x0_dev *dev); -int mt76x0_mcu_cmd_init(struct mt76x0_dev *dev); -void mt76x0_mcu_cmd_deinit(struct mt76x0_dev *dev); - -int -mt76x0_mcu_calibrate(struct mt76x0_dev *dev, enum mcu_calibrate cal, u32 val); - -int -mt76x0_mcu_function_select(struct mt76x0_dev *dev, enum mcu_function func, u32 val); - #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h b/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h index fc9857f61771..6aaa9a5b51db 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h @@ -26,7 +26,8 @@ #include <linux/debugfs.h> #include "../mt76.h" -#include "regs.h" +#include "../mt76x02_regs.h" +#include "../mt76x02_mac.h" #define MT_CALIBRATE_INTERVAL (4 * HZ) @@ -38,29 +39,6 @@ #define MT_USB_AGGR_SIZE_LIMIT 21 /* * 1024B */ #define MT_USB_AGGR_TIMEOUT 0x80 /* * 33ns */ -#define MT_RX_ORDER 3 -#define MT_RX_URB_SIZE (PAGE_SIZE << MT_RX_ORDER) - -struct mt76x0_dma_buf { - struct urb *urb; - void *buf; - dma_addr_t dma; - size_t len; -}; - -struct mt76x0_mcu { - struct mutex mutex; - - u8 msg_seq; - - struct mt76x0_dma_buf resp; - struct completion resp_cmpl; - - struct mt76_reg_pair *reg_pairs; - unsigned int reg_pairs_len; - u32 reg_base; - bool burst_read; -}; struct mac_stats { u64 rx_stat[6]; @@ -70,48 +48,6 @@ struct mac_stats { u64 zero_len_del[2]; }; -#define N_RX_ENTRIES 16 -struct mt76x0_rx_queue { - struct mt76x0_dev *dev; - - struct mt76x0_dma_buf_rx { - struct urb *urb; - struct page *p; - } e[N_RX_ENTRIES]; - - unsigned int start; - unsigned int end; - unsigned int entries; - unsigned int pending; -}; - -#define N_TX_ENTRIES 64 - -struct mt76x0_tx_queue { - struct mt76x0_dev *dev; - - struct mt76x0_dma_buf_tx { - struct urb *urb; - struct sk_buff *skb; - } e[N_TX_ENTRIES]; - - unsigned int start; - unsigned int end; - unsigned int entries; - unsigned int used; - unsigned int fifo_seq; -}; - -/* WCID allocation: - * 0: mcast wcid - * 1: bssid wcid - * 1...: STAs - * ...7e: group wcids - * 7f: reserved - */ -#define N_WCIDS 128 -#define GROUP_WCID(idx) (254 - idx) - struct mt76x0_eeprom_params; #define MT_EE_TEMPERATURE_SLOPE 39 @@ -133,9 +69,6 @@ enum mt_bw { * struct mt76x0_dev - adapter structure * @lock: protects @wcid->tx_rate. * @mac_lock: locks out mac80211's tx status and rx paths. - * @tx_lock: protects @tx_q and changes of MT76_STATE_*_STATS - * flags in @state. - * @rx_lock: protects @rx_q. * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi. * @mutex: ensures exclusive access from mac80211 callbacks. * @reg_atomic_mutex: ensures atomicity of indirect register accesses @@ -146,57 +79,24 @@ enum mt_bw { struct mt76x0_dev { struct mt76_dev mt76; /* must be first */ - struct mutex mutex; - - struct mutex usb_ctrl_mtx; u8 data[32]; - struct tasklet_struct rx_tasklet; - struct tasklet_struct tx_tasklet; - - u8 out_ep[__MT_EP_OUT_MAX]; - u16 out_max_packet; - u8 in_ep[__MT_EP_IN_MAX]; - u16 in_max_packet; - - unsigned long wcid_mask[DIV_ROUND_UP(N_WCIDS, BITS_PER_LONG)]; - unsigned long vif_mask; - - struct mt76x0_mcu mcu; - struct delayed_work cal_work; struct delayed_work mac_work; - struct workqueue_struct *stat_wq; - struct delayed_work stat_work; - - struct mt76_wcid *mon_wcid; - struct mt76_wcid __rcu *wcid[N_WCIDS]; - spinlock_t mac_lock; const u16 *beacon_offsets; - u8 macaddr[ETH_ALEN]; struct mt76x0_eeprom_params *ee; struct mutex reg_atomic_mutex; struct mutex hw_atomic_mutex; - u32 rxfilter; u32 debugfs_reg; - /* TX */ - spinlock_t tx_lock; - struct mt76x0_tx_queue *tx_q; - struct sk_buff_head tx_skb_done; - atomic_t avg_ampdu_len; - /* RX */ - spinlock_t rx_lock; - struct mt76x0_rx_queue rx_q; - /* Connection monitoring things */ spinlock_t con_mon_lock; u8 ap_bssid[ETH_ALEN]; @@ -212,47 +112,6 @@ struct mt76x0_dev { struct mac_stats stats; }; -struct mt76x0_wcid { - u8 idx; - u8 hw_key_idx; - - u16 tx_rate; - bool tx_rate_set; - u8 tx_rate_nss; -}; - -struct mt76_vif { - u8 idx; - - struct mt76_wcid group_wcid; -}; - -struct mt76_tx_status { - u8 valid:1; - u8 success:1; - u8 aggr:1; - u8 ack_req:1; - u8 is_probe:1; - u8 wcid; - u8 pktid; - u8 retry; - u16 rate; -} __packed __aligned(2); - -struct mt76_sta { - struct mt76_wcid wcid; - struct mt76_tx_status status; - int n_frames; - u16 agg_ssn[IEEE80211_NUM_TIDS]; -}; - -struct mt76_reg_pair { - u32 reg; - u32 value; -}; - -struct mt76x0_rxwi; - extern const struct ieee80211_ops mt76x0_ops; static inline bool is_mt7610e(struct mt76x0_dev *dev) @@ -263,22 +122,13 @@ static inline bool is_mt7610e(struct mt76x0_dev *dev) void mt76x0_init_debugfs(struct mt76x0_dev *dev); -int mt76x0_wait_asic_ready(struct mt76x0_dev *dev); - /* Compatibility with mt76 */ #define mt76_rmw_field(_dev, _reg, _field, _val) \ mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val)) -int mt76x0_write_reg_pairs(struct mt76x0_dev *dev, u32 base, - const struct mt76_reg_pair *data, int len); -int mt76x0_read_reg_pairs(struct mt76x0_dev *dev, u32 base, - struct mt76_reg_pair *data, int len); -int mt76x0_burst_write_regs(struct mt76x0_dev *dev, u32 offset, - const u32 *data, int n); -void mt76x0_addr_wr(struct mt76x0_dev *dev, const u32 offset, const u8 *addr); - /* Init */ -struct mt76x0_dev *mt76x0_alloc_device(struct device *dev); +struct mt76x0_dev * +mt76x0_alloc_device(struct device *pdev, const struct mt76_driver_ops *drv_ops); int mt76x0_init_hardware(struct mt76x0_dev *dev); int mt76x0_register_device(struct mt76x0_dev *dev); void mt76x0_cleanup(struct mt76x0_dev *dev); @@ -295,7 +145,7 @@ void mt76x0_agc_restore(struct mt76x0_dev *dev); int mt76x0_phy_set_channel(struct mt76x0_dev *dev, struct cfg80211_chan_def *chandef); void mt76x0_phy_recalibrate_after_assoc(struct mt76x0_dev *dev); -int mt76x0_phy_get_rssi(struct mt76x0_dev *dev, struct mt76x0_rxwi *rxwi); +int mt76x0_phy_get_rssi(struct mt76x0_dev *dev, struct mt76x02_rxwi *rxwi); void mt76x0_phy_con_cal_onoff(struct mt76x0_dev *dev, struct ieee80211_bss_conf *info); @@ -305,26 +155,17 @@ void mt76x0_mac_set_protection(struct mt76x0_dev *dev, bool legacy_prot, int ht_mode); void mt76x0_mac_set_short_preamble(struct mt76x0_dev *dev, bool short_preamb); void mt76x0_mac_config_tsf(struct mt76x0_dev *dev, bool enable, int interval); -void -mt76x0_mac_wcid_setup(struct mt76x0_dev *dev, u8 idx, u8 vif_idx, u8 *mac); void mt76x0_mac_set_ampdu_factor(struct mt76x0_dev *dev); /* TX */ void mt76x0_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); -int mt76x0_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u16 queue, const struct ieee80211_tx_queue_params *params); -void mt76x0_tx_status(struct mt76x0_dev *dev, struct sk_buff *skb); -void mt76x0_tx_stat(struct work_struct *work); - -/* util */ -void mt76x0_remove_hdr_pad(struct sk_buff *skb); -int mt76x0_insert_hdr_pad(struct sk_buff *skb); - -int mt76x0_dma_init(struct mt76x0_dev *dev); -void mt76x0_dma_cleanup(struct mt76x0_dev *dev); -int mt76x0_dma_enqueue_tx(struct mt76x0_dev *dev, struct sk_buff *skb, - struct mt76_wcid *wcid, int hw_q); +void mt76x0_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb); +int mt76x0_tx_prepare_skb(struct mt76_dev *mdev, void *data, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + u32 *tx_info); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c new file mode 100644 index 000000000000..eb383f96ec9a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -0,0 +1,81 @@ +/* + * 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/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "mt76x0.h" + +static int +mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct mt76x0_dev *dev; + int ret = -ENODEV; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + dev = mt76x0_alloc_device(&pdev->dev, NULL); + if (!dev) + return -ENOMEM; + + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); + + dev->mt76.rev = mt76_rr(dev, MT_ASIC_VERSION); + dev_info(dev->mt76.dev, "ASIC revision: %08x\n", dev->mt76.rev); + +/* error: */ + ieee80211_free_hw(mt76_hw(dev)); + return ret; +} + +static void +mt76x0e_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + + mt76_unregister_device(mdev); + ieee80211_free_hw(mdev->hw); +} + +static const struct pci_device_id mt76x0e_device_table[] = { + { PCI_DEVICE(0x14c3, 0x7630) }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, mt76x0e_device_table); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct pci_driver mt76x0e_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76x0e_device_table, + .probe = mt76x0e_probe, + .remove = mt76x0e_remove, +}; + +module_pci_driver(mt76x0e_driver); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c index 5da7bfbe907f..2b6d928aab89 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -117,7 +117,7 @@ rf_wr(struct mt76x0_dev *dev, u32 offset, u8 val) .value = val, }; - return mt76x0_write_reg_pairs(dev, MT_MCU_MEMMAP_RF, &pair, 1); + return mt76_wr_rp(dev, MT_MCU_MEMMAP_RF, &pair, 1); } else { WARN_ON_ONCE(1); return mt76x0_rf_csr_wr(dev, offset, val); @@ -135,7 +135,7 @@ rf_rr(struct mt76x0_dev *dev, u32 offset) .reg = offset, }; - ret = mt76x0_read_reg_pairs(dev, MT_MCU_MEMMAP_RF, &pair, 1); + ret = mt76_rd_rp(dev, MT_MCU_MEMMAP_RF, &pair, 1); val = pair.value; } else { WARN_ON_ONCE(1); @@ -175,8 +175,9 @@ rf_clear(struct mt76x0_dev *dev, u32 offset, u8 mask) } #endif -#define RF_RANDOM_WRITE(dev, tab) \ - mt76x0_write_reg_pairs(dev, MT_MCU_MEMMAP_RF, tab, ARRAY_SIZE(tab)); +#define RF_RANDOM_WRITE(dev, tab) \ + mt76_wr_rp(dev, MT_MCU_MEMMAP_RF, \ + tab, ARRAY_SIZE(tab)) int mt76x0_wait_bbp_ready(struct mt76x0_dev *dev) { @@ -225,7 +226,7 @@ mt76x0_bbp_set_ctrlch(struct mt76x0_dev *dev, enum nl80211_chan_width width, mt76_rmw_field(dev, MT_BBP(TXBE, 0), MT_BBP_TXBE_R0_CTRL_CHAN, ctrl); } -int mt76x0_phy_get_rssi(struct mt76x0_dev *dev, struct mt76x0_rxwi *rxwi) +int mt76x0_phy_get_rssi(struct mt76x0_dev *dev, struct mt76x02_rxwi *rxwi) { s8 lna_gain, rssi_offset; int val; @@ -640,7 +641,7 @@ mt76x0_bbp_set_bw(struct mt76x0_dev *dev, enum nl80211_chan_width width) return ; } - mt76x0_mcu_function_select(dev, BW_SETTING, bw); + mt76x02_mcu_function_select(&dev->mt76, BW_SETTING, bw, false); } static void @@ -757,10 +758,10 @@ __mt76x0_phy_set_channel(struct mt76x0_dev *dev, /* Vendor driver don't do it */ /* mt76x0_phy_set_tx_power(dev, channel, rf_bw_band); */ + mt76x0_vco_cal(dev, channel); if (scan) - mt76x0_vco_cal(dev, channel); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXDCOC, 1, false); - mt76x0_mcu_calibrate(dev, MCU_CAL_RXDCOC, 1); mt76x0_phy_set_chan_pwr(dev, channel); dev->mt76.chandef = *chandef; @@ -785,7 +786,7 @@ void mt76x0_phy_recalibrate_after_assoc(struct mt76x0_dev *dev) u8 channel = dev->mt76.chandef.chan->hw_value; int is_5ghz = (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) ? 1 : 0; - mt76x0_mcu_calibrate(dev, MCU_CAL_R, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_R, 0, false); mt76x0_vco_cal(dev, channel); @@ -797,20 +798,22 @@ void mt76x0_phy_recalibrate_after_assoc(struct mt76x0_dev *dev) reg_val &= 0xffffff7e; mt76_wr(dev, 0x2124, reg_val); - mt76x0_mcu_calibrate(dev, MCU_CAL_RXDCOC, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXDCOC, 0, false); - mt76x0_mcu_calibrate(dev, MCU_CAL_LC, is_5ghz); - mt76x0_mcu_calibrate(dev, MCU_CAL_LOFT, is_5ghz); - mt76x0_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz); - mt76x0_mcu_calibrate(dev, MCU_CAL_TX_GROUP_DELAY, is_5ghz); - mt76x0_mcu_calibrate(dev, MCU_CAL_RXIQ, is_5ghz); - mt76x0_mcu_calibrate(dev, MCU_CAL_RX_GROUP_DELAY, is_5ghz); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_LC, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_LOFT, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TXIQ, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TX_GROUP_DELAY, + is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXIQ, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RX_GROUP_DELAY, + is_5ghz, false); mt76_wr(dev, 0x2124, reg_val); mt76_wr(dev, MT_TX_ALC_CFG_0, tx_alc); msleep(100); - mt76x0_mcu_calibrate(dev, MCU_CAL_RXDCOC, 1); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXDCOC, 1, false); } void mt76x0_agc_save(struct mt76x0_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/regs.h b/drivers/net/wireless/mediatek/mt76/mt76x0/regs.h deleted file mode 100644 index 16bed4aaa242..000000000000 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/regs.h +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> - * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> - * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * 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. - */ - -#ifndef __MT76_REGS_H -#define __MT76_REGS_H - -#include <linux/bitops.h> - -#define MT_ASIC_VERSION 0x0000 - -#define MT76XX_REV_E3 0x22 -#define MT76XX_REV_E4 0x33 - -#define MT_CMB_CTRL 0x0020 -#define MT_CMB_CTRL_XTAL_RDY BIT(22) -#define MT_CMB_CTRL_PLL_LD BIT(23) - -#define MT_EFUSE_CTRL 0x0024 -#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) -#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) -#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) -#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) -#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) -#define MT_EFUSE_CTRL_KICK BIT(30) -#define MT_EFUSE_CTRL_SEL BIT(31) - -#define MT_EFUSE_DATA_BASE 0x0028 -#define MT_EFUSE_DATA(_n) (MT_EFUSE_DATA_BASE + ((_n) << 2)) - -#define MT_COEXCFG0 0x0040 -#define MT_COEXCFG0_COEX_EN BIT(0) - -#define MT_COEXCFG3 0x004c - -#define MT_LDO_CTRL_0 0x006c -#define MT_LDO_CTRL_1 0x0070 - -#define MT_WLAN_FUN_CTRL 0x0080 -#define MT_WLAN_FUN_CTRL_WLAN_EN BIT(0) -#define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1) -#define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2) - -#define MT_WLAN_FUN_CTRL_WLAN_RESET BIT(3) /* MT76x0 */ -#define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) /* MT76x2 */ - -#define MT_WLAN_FUN_CTRL_PCIE_CLK_REQ BIT(4) -#define MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL BIT(5) -#define MT_WLAN_FUN_CTRL_INV_ANT_SEL BIT(6) -#define MT_WLAN_FUN_CTRL_WAKE_HOST BIT(7) - -#define MT_WLAN_FUN_CTRL_THERM_RST BIT(8) /* MT76x2 */ -#define MT_WLAN_FUN_CTRL_THERM_CKEN BIT(9) /* MT76x2 */ - -#define MT_WLAN_FUN_CTRL_GPIO_IN GENMASK(15, 8) /* MT76x0 */ -#define MT_WLAN_FUN_CTRL_GPIO_OUT GENMASK(23, 16) /* MT76x0 */ -#define MT_WLAN_FUN_CTRL_GPIO_OUT_EN GENMASK(31, 24) /* MT76x0 */ - -#define MT_XO_CTRL0 0x0100 -#define MT_XO_CTRL1 0x0104 -#define MT_XO_CTRL2 0x0108 -#define MT_XO_CTRL3 0x010c -#define MT_XO_CTRL4 0x0110 - -#define MT_XO_CTRL5 0x0114 -#define MT_XO_CTRL5_C2_VAL GENMASK(14, 8) - -#define MT_XO_CTRL6 0x0118 -#define MT_XO_CTRL6_C2_CTRL GENMASK(14, 8) - -#define MT_XO_CTRL7 0x011c - -#define MT_IOCFG_6 0x0124 -#define MT_WLAN_MTC_CTRL 0x10148 -#define MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP BIT(0) -#define MT_WLAN_MTC_CTRL_PWR_ACK BIT(12) -#define MT_WLAN_MTC_CTRL_PWR_ACK_S BIT(13) -#define MT_WLAN_MTC_CTRL_BBP_MEM_PD GENMASK(19, 16) -#define MT_WLAN_MTC_CTRL_PBF_MEM_PD BIT(20) -#define MT_WLAN_MTC_CTRL_FCE_MEM_PD BIT(21) -#define MT_WLAN_MTC_CTRL_TSO_MEM_PD BIT(22) -#define MT_WLAN_MTC_CTRL_BBP_MEM_RB BIT(24) -#define MT_WLAN_MTC_CTRL_PBF_MEM_RB BIT(25) -#define MT_WLAN_MTC_CTRL_FCE_MEM_RB BIT(26) -#define MT_WLAN_MTC_CTRL_TSO_MEM_RB BIT(27) -#define MT_WLAN_MTC_CTRL_STATE_UP BIT(28) - -#define MT_INT_SOURCE_CSR 0x0200 -#define MT_INT_MASK_CSR 0x0204 - -#define MT_INT_RX_DONE(_n) BIT(_n) -#define MT_INT_RX_DONE_ALL GENMASK(1, 0) -#define MT_INT_TX_DONE_ALL GENMASK(13, 4) -#define MT_INT_TX_DONE(_n) BIT(_n + 4) -#define MT_INT_RX_COHERENT BIT(16) -#define MT_INT_TX_COHERENT BIT(17) -#define MT_INT_ANY_COHERENT BIT(18) -#define MT_INT_MCU_CMD BIT(19) -#define MT_INT_TBTT BIT(20) -#define MT_INT_PRE_TBTT BIT(21) -#define MT_INT_TX_STAT BIT(22) -#define MT_INT_AUTO_WAKEUP BIT(23) -#define MT_INT_GPTIMER BIT(24) -#define MT_INT_RXDELAYINT BIT(26) -#define MT_INT_TXDELAYINT BIT(27) - -#define MT_WPDMA_GLO_CFG 0x0208 -#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) -#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) -#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) -#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) -#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) -#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) -#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) -#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) -#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) -#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) - -#define MT_WPDMA_RST_IDX 0x020c - -#define MT_WPDMA_DELAY_INT_CFG 0x0210 - -#define MT_WMM_AIFSN 0x0214 -#define MT_WMM_AIFSN_MASK GENMASK(3, 0) -#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) - -#define MT_WMM_CWMIN 0x0218 -#define MT_WMM_CWMIN_MASK GENMASK(3, 0) -#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 4) - -#define MT_WMM_CWMAX 0x021c -#define MT_WMM_CWMAX_MASK GENMASK(3, 0) -#define MT_WMM_CWMAX_SHIFT(_n) ((_n) * 4) - -#define MT_WMM_TXOP_BASE 0x0220 -#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + (((_n) / 2) << 2)) -#define MT_WMM_TXOP_SHIFT(_n) ((_n & 1) * 16) -#define MT_WMM_TXOP_MASK GENMASK(15, 0) - -#define MT_WMM_CTRL 0x0230 /* MT76x0 */ - -#define MT_FCE_DMA_ADDR 0x0230 -#define MT_FCE_DMA_LEN 0x0234 - -#define MT_USB_DMA_CFG 0x238 -#define MT_USB_DMA_CFG_RX_BULK_AGG_TOUT GENMASK(7, 0) -#define MT_USB_DMA_CFG_RX_BULK_AGG_LMT GENMASK(15, 8) -#define MT_USB_DMA_CFG_TX_WL_DROP BIT(16) -#define MT_USB_DMA_CFG_WAKEUP_EN BIT(17) -#define MT_USB_DMA_CFG_RX_DROP_OR_PADDING BIT(18) -#define MT_USB_DMA_CFG_TX_CLR BIT(19) -#define MT_USB_DMA_CFG_WL_LPK_EN BIT(20) -#define MT_USB_DMA_CFG_RX_BULK_AGG_EN BIT(21) -#define MT_USB_DMA_CFG_RX_BULK_EN BIT(22) -#define MT_USB_DMA_CFG_TX_BULK_EN BIT(23) -#define MT_USB_DMA_CFG_EP_OUT_VALID GENMASK(29, 24) -#define MT_USB_DMA_CFG_RX_BUSY BIT(30) -#define MT_USB_DMA_CFG_TX_BUSY BIT(31) -#if 0 -#define MT_USB_DMA_CFG_TX_CLR BIT(19) -#define MT_USB_DMA_CFG_TXOP_HALT BIT(20) -#define MT_USB_DMA_CFG_RX_BULK_AGG_EN BIT(21) -#define MT_USB_DMA_CFG_RX_BULK_EN BIT(22) -#define MT_USB_DMA_CFG_TX_BULK_EN BIT(23) -#define MT_USB_DMA_CFG_UDMA_RX_WL_DROP BIT(25) -#endif - -#define MT_TSO_CTRL 0x0250 -#define MT_HEADER_TRANS_CTRL_REG 0x0260 - -#define MT_US_CYC_CFG 0x02a4 -#define MT_US_CYC_CNT GENMASK(7, 0) - -#define MT_TX_RING_BASE 0x0300 -#define MT_RX_RING_BASE 0x03c0 -#define MT_RING_SIZE 0x10 - -#define MT_TX_HW_QUEUE_MCU 8 -#define MT_TX_HW_QUEUE_MGMT 9 - -#define MT_PBF_SYS_CTRL 0x0400 -#define MT_PBF_SYS_CTRL_MCU_RESET BIT(0) -#define MT_PBF_SYS_CTRL_DMA_RESET BIT(1) -#define MT_PBF_SYS_CTRL_MAC_RESET BIT(2) -#define MT_PBF_SYS_CTRL_PBF_RESET BIT(3) -#define MT_PBF_SYS_CTRL_ASY_RESET BIT(4) - -#define MT_PBF_CFG 0x0404 -#define MT_PBF_CFG_TX0Q_EN BIT(0) -#define MT_PBF_CFG_TX1Q_EN BIT(1) -#define MT_PBF_CFG_TX2Q_EN BIT(2) -#define MT_PBF_CFG_TX3Q_EN BIT(3) -#define MT_PBF_CFG_RX0Q_EN BIT(4) -#define MT_PBF_CFG_RX_DROP_EN BIT(8) - -#define MT_PBF_TX_MAX_PCNT 0x0408 -#define MT_PBF_RX_MAX_PCNT 0x040c - -#define MT_BCN_OFFSET_BASE 0x041c -#define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2)) - -#define MT_RXQ_STA 0x0430 -#define MT_TXQ_STA 0x0434 -#define MT_RF_CSR_CFG 0x0500 -#define MT_RF_CSR_CFG_DATA GENMASK(7, 0) -#define MT_RF_CSR_CFG_REG_ID GENMASK(13, 8) -#define MT_RF_CSR_CFG_REG_BANK GENMASK(17, 14) -#define MT_RF_CSR_CFG_WR BIT(30) -#define MT_RF_CSR_CFG_KICK BIT(31) - -#define MT_RF_BYPASS_0 0x0504 -#define MT_RF_BYPASS_1 0x0508 -#define MT_RF_SETTING_0 0x050c - -#define MT_RF_MISC 0x0518 -#define MT_RF_DATA_WRITE 0x0524 - -#define MT_RF_CTRL 0x0528 -#define MT_RF_CTRL_ADDR GENMASK(11, 0) -#define MT_RF_CTRL_WRITE BIT(12) -#define MT_RF_CTRL_BUSY BIT(13) -#define MT_RF_CTRL_IDX BIT(16) - -#define MT_RF_DATA_READ 0x052c - -#define MT_COM_REG0 0x0730 -#define MT_COM_REG1 0x0734 -#define MT_COM_REG2 0x0738 -#define MT_COM_REG3 0x073C - -#define MT_FCE_PSE_CTRL 0x0800 -#define MT_FCE_PARAMETERS 0x0804 -#define MT_FCE_CSO 0x0808 - -#define MT_FCE_L2_STUFF 0x080c -#define MT_FCE_L2_STUFF_HT_L2_EN BIT(0) -#define MT_FCE_L2_STUFF_QOS_L2_EN BIT(1) -#define MT_FCE_L2_STUFF_RX_STUFF_EN BIT(2) -#define MT_FCE_L2_STUFF_TX_STUFF_EN BIT(3) -#define MT_FCE_L2_STUFF_WR_MPDU_LEN_EN BIT(4) -#define MT_FCE_L2_STUFF_MVINV_BSWAP BIT(5) -#define MT_FCE_L2_STUFF_TS_CMD_QSEL_EN GENMASK(15, 8) -#define MT_FCE_L2_STUFF_TS_LEN_EN GENMASK(23, 16) -#define MT_FCE_L2_STUFF_OTHER_PORT GENMASK(25, 24) - -#define MT_FCE_WLAN_FLOW_CONTROL1 0x0824 - -#define MT_TX_CPU_FROM_FCE_BASE_PTR 0x09a0 -#define MT_TX_CPU_FROM_FCE_MAX_COUNT 0x09a4 -#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 - -#define MT_FCE_PDMA_GLOBAL_CONF 0x09c4 - -#define MT_PAUSE_ENABLE_CONTROL1 0x0a38 - -#define MT_FCE_SKIP_FS 0x0a6c - -#define MT_MAC_CSR0 0x1000 -#define MT_MAC_SYS_CTRL 0x1004 -#define MT_MAC_SYS_CTRL_RESET_CSR BIT(0) -#define MT_MAC_SYS_CTRL_RESET_BBP BIT(1) -#define MT_MAC_SYS_CTRL_ENABLE_TX BIT(2) -#define MT_MAC_SYS_CTRL_ENABLE_RX BIT(3) - -#define MT_MAC_ADDR_DW0 0x1008 -#define MT_MAC_ADDR_DW1 0x100c -#define MT_MAC_ADDR_DW1_U2ME_MASK GENMASK(23, 16) - -#define MT_MAC_BSSID_DW0 0x1010 -#define MT_MAC_BSSID_DW1 0x1014 -#define MT_MAC_BSSID_DW1_ADDR GENMASK(15, 0) -#define MT_MAC_BSSID_DW1_MBSS_MODE GENMASK(17, 16) -#define MT_MAC_BSSID_DW1_MBEACON_N GENMASK(20, 18) -#define MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT BIT(21) -#define MT_MAC_BSSID_DW1_MBSS_MODE_B2 BIT(22) -#define MT_MAC_BSSID_DW1_MBEACON_N_B3 BIT(23) -#define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24) - -#define MT_MAX_LEN_CFG 0x1018 -#define MT_MAX_LEN_CFG_AMPDU GENMASK(13, 12) - -#define MT_LED_CFG 0x102c - -#define MT_AMPDU_MAX_LEN_20M1S 0x1030 -#define MT_AMPDU_MAX_LEN_20M2S 0x1034 -#define MT_AMPDU_MAX_LEN_40M1S 0x1038 -#define MT_AMPDU_MAX_LEN_40M2S 0x103c -#define MT_AMPDU_MAX_LEN 0x1040 - -#define MT_WCID_DROP_BASE 0x106c -#define MT_WCID_DROP(_n) (MT_WCID_DROP_BASE + ((_n) >> 5) * 4) -#define MT_WCID_DROP_MASK(_n) BIT((_n) % 32) - -#define MT_BCN_BYPASS_MASK 0x108c - -#define MT_MAC_APC_BSSID_BASE 0x1090 -#define MT_MAC_APC_BSSID_L(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8)) -#define MT_MAC_APC_BSSID_H(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8 + 4)) -#define MT_MAC_APC_BSSID_H_ADDR GENMASK(15, 0) -#define MT_MAC_APC_BSSID0_H_EN BIT(16) - -#define MT_XIFS_TIME_CFG 0x1100 -#define MT_XIFS_TIME_CFG_CCK_SIFS GENMASK(7, 0) -#define MT_XIFS_TIME_CFG_OFDM_SIFS GENMASK(15, 8) -#define MT_XIFS_TIME_CFG_OFDM_XIFS GENMASK(19, 16) -#define MT_XIFS_TIME_CFG_EIFS GENMASK(28, 20) -#define MT_XIFS_TIME_CFG_BB_RXEND_EN BIT(29) - -#define MT_BKOFF_SLOT_CFG 0x1104 -#define MT_BKOFF_SLOT_CFG_SLOTTIME GENMASK(7, 0) -#define MT_BKOFF_SLOT_CFG_CC_DELAY GENMASK(11, 8) - -#define MT_BEACON_TIME_CFG 0x1114 -#define MT_BEACON_TIME_CFG_INTVAL GENMASK(15, 0) -#define MT_BEACON_TIME_CFG_TIMER_EN BIT(16) -#define MT_BEACON_TIME_CFG_SYNC_MODE GENMASK(18, 17) -#define MT_BEACON_TIME_CFG_TBTT_EN BIT(19) -#define MT_BEACON_TIME_CFG_BEACON_TX BIT(20) -#define MT_BEACON_TIME_CFG_TSF_COMP GENMASK(31, 24) - -#define MT_TBTT_SYNC_CFG 0x1118 -#define MT_TBTT_TIMER_CFG 0x1124 - -#define MT_INT_TIMER_CFG 0x1128 -#define MT_INT_TIMER_CFG_PRE_TBTT GENMASK(15, 0) -#define MT_INT_TIMER_CFG_GP_TIMER GENMASK(31, 16) - -#define MT_INT_TIMER_EN 0x112c -#define MT_INT_TIMER_EN_PRE_TBTT_EN BIT(0) -#define MT_INT_TIMER_EN_GP_TIMER_EN BIT(1) - -#define MT_MAC_STATUS 0x1200 -#define MT_MAC_STATUS_TX BIT(0) -#define MT_MAC_STATUS_RX BIT(1) - -#define MT_PWR_PIN_CFG 0x1204 -#define MT_AUX_CLK_CFG 0x120c - -#define MT_BB_PA_MODE_CFG0 0x1214 -#define MT_BB_PA_MODE_CFG1 0x1218 -#define MT_RF_PA_MODE_CFG0 0x121c -#define MT_RF_PA_MODE_CFG1 0x1220 - -#define MT_RF_PA_MODE_ADJ0 0x1228 -#define MT_RF_PA_MODE_ADJ1 0x122c - -#define MT_DACCLK_EN_DLY_CFG 0x1264 - -#define MT_EDCA_CFG_BASE 0x1300 -#define MT_EDCA_CFG_AC(_n) (MT_EDCA_CFG_BASE + ((_n) << 2)) -#define MT_EDCA_CFG_TXOP GENMASK(7, 0) -#define MT_EDCA_CFG_AIFSN GENMASK(11, 8) -#define MT_EDCA_CFG_CWMIN GENMASK(15, 12) -#define MT_EDCA_CFG_CWMAX GENMASK(19, 16) - -#define MT_TX_PWR_CFG_0 0x1314 -#define MT_TX_PWR_CFG_1 0x1318 -#define MT_TX_PWR_CFG_2 0x131c -#define MT_TX_PWR_CFG_3 0x1320 -#define MT_TX_PWR_CFG_4 0x1324 - -#define MT_TX_BAND_CFG 0x132c -#define MT_TX_BAND_CFG_UPPER_40M BIT(0) -#define MT_TX_BAND_CFG_5G BIT(1) -#define MT_TX_BAND_CFG_2G BIT(2) - -#define MT_HT_FBK_TO_LEGACY 0x1384 -#define MT_TX_MPDU_ADJ_INT 0x1388 - -#define MT_TX_PWR_CFG_7 0x13d4 -#define MT_TX_PWR_CFG_8 0x13d8 -#define MT_TX_PWR_CFG_9 0x13dc - -#define MT_TX_SW_CFG0 0x1330 -#define MT_TX_SW_CFG1 0x1334 -#define MT_TX_SW_CFG2 0x1338 - -#define MT_TXOP_CTRL_CFG 0x1340 -#define MT_TXOP_TRUN_EN GENMASK(5, 0) -#define MT_TXOP_EXT_CCA_DLY GENMASK(15, 8) -#define MT_TXOP_CTRL - -#define MT_TX_RTS_CFG 0x1344 -#define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0) -#define MT_TX_RTS_CFG_THRESH GENMASK(23, 8) -#define MT_TX_RTS_FALLBACK BIT(24) - -#define MT_TX_TIMEOUT_CFG 0x1348 -#define MT_TX_RETRY_CFG 0x134c -#define MT_TX_LINK_CFG 0x1350 -#define MT_HT_FBK_CFG0 0x1354 -#define MT_HT_FBK_CFG1 0x1358 -#define MT_LG_FBK_CFG0 0x135c -#define MT_LG_FBK_CFG1 0x1360 - -#define MT_CCK_PROT_CFG 0x1364 -#define MT_OFDM_PROT_CFG 0x1368 -#define MT_MM20_PROT_CFG 0x136c -#define MT_MM40_PROT_CFG 0x1370 -#define MT_GF20_PROT_CFG 0x1374 -#define MT_GF40_PROT_CFG 0x1378 - -#define MT_PROT_RATE GENMASK(15, 0) -#define MT_PROT_CTRL_RTS_CTS BIT(16) -#define MT_PROT_CTRL_CTS2SELF BIT(17) -#define MT_PROT_NAV_SHORT BIT(18) -#define MT_PROT_NAV_LONG BIT(19) -#define MT_PROT_TXOP_ALLOW_CCK BIT(20) -#define MT_PROT_TXOP_ALLOW_OFDM BIT(21) -#define MT_PROT_TXOP_ALLOW_MM20 BIT(22) -#define MT_PROT_TXOP_ALLOW_MM40 BIT(23) -#define MT_PROT_TXOP_ALLOW_GF20 BIT(24) -#define MT_PROT_TXOP_ALLOW_GF40 BIT(25) -#define MT_PROT_RTS_THR_EN BIT(26) -#define MT_PROT_RATE_CCK_11 0x0003 -#define MT_PROT_RATE_OFDM_6 0x4000 -#define MT_PROT_RATE_OFDM_24 0x4004 -#define MT_PROT_RATE_DUP_OFDM_24 0x4084 -#define MT_PROT_TXOP_ALLOW_ALL GENMASK(25, 20) -#define MT_PROT_TXOP_ALLOW_BW20 (MT_PROT_TXOP_ALLOW_ALL & \ - ~MT_PROT_TXOP_ALLOW_MM40 & \ - ~MT_PROT_TXOP_ALLOW_GF40) - -#define MT_EXP_ACK_TIME 0x1380 - -#define MT_TX_PWR_CFG_0_EXT 0x1390 -#define MT_TX_PWR_CFG_1_EXT 0x1394 - -#define MT_TX_FBK_LIMIT 0x1398 -#define MT_TX_FBK_LIMIT_MPDU_FBK GENMASK(7, 0) -#define MT_TX_FBK_LIMIT_AMPDU_FBK GENMASK(15, 8) -#define MT_TX_FBK_LIMIT_MPDU_UP_CLEAR BIT(16) -#define MT_TX_FBK_LIMIT_AMPDU_UP_CLEAR BIT(17) -#define MT_TX_FBK_LIMIT_RATE_LUT BIT(18) - -#define MT_TX0_RF_GAIN_CORR 0x13a0 -#define MT_TX1_RF_GAIN_CORR 0x13a4 -#define MT_TX0_RF_GAIN_ATTEN 0x13a8 - -#define MT_TX_ALC_CFG_0 0x13b0 -#define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0) -#define MT_TX_ALC_CFG_0_CH_INIT_1 GENMASK(13, 8) -#define MT_TX_ALC_CFG_0_LIMIT_0 GENMASK(21, 16) -#define MT_TX_ALC_CFG_0_LIMIT_1 GENMASK(29, 24) - -#define MT_TX_ALC_CFG_1 0x13b4 -#define MT_TX_ALC_CFG_1_TEMP_COMP GENMASK(5, 0) - -#define MT_TX_ALC_CFG_2 0x13a8 -#define MT_TX_ALC_CFG_2_TEMP_COMP GENMASK(5, 0) - -#define MT_TX0_BB_GAIN_ATTEN 0x13c0 - -#define MT_TX_ALC_VGA3 0x13c8 - -#define MT_TX_PROT_CFG6 0x13e0 -#define MT_TX_PROT_CFG7 0x13e4 -#define MT_TX_PROT_CFG8 0x13e8 - -#define MT_PIFS_TX_CFG 0x13ec - -#define MT_RX_FILTR_CFG 0x1400 - -#define MT_RX_FILTR_CFG_CRC_ERR BIT(0) -#define MT_RX_FILTR_CFG_PHY_ERR BIT(1) -#define MT_RX_FILTR_CFG_PROMISC BIT(2) -#define MT_RX_FILTR_CFG_OTHER_BSS BIT(3) -#define MT_RX_FILTR_CFG_VER_ERR BIT(4) -#define MT_RX_FILTR_CFG_MCAST BIT(5) -#define MT_RX_FILTR_CFG_BCAST BIT(6) -#define MT_RX_FILTR_CFG_DUP BIT(7) -#define MT_RX_FILTR_CFG_CFACK BIT(8) -#define MT_RX_FILTR_CFG_CFEND BIT(9) -#define MT_RX_FILTR_CFG_ACK BIT(10) -#define MT_RX_FILTR_CFG_CTS BIT(11) -#define MT_RX_FILTR_CFG_RTS BIT(12) -#define MT_RX_FILTR_CFG_PSPOLL BIT(13) -#define MT_RX_FILTR_CFG_BA BIT(14) -#define MT_RX_FILTR_CFG_BAR BIT(15) -#define MT_RX_FILTR_CFG_CTRL_RSV BIT(16) - -#define MT_AUTO_RSP_CFG 0x1404 - -#define MT_AUTO_RSP_PREAMB_SHORT BIT(4) - -#define MT_LEGACY_BASIC_RATE 0x1408 -#define MT_HT_BASIC_RATE 0x140c -#define MT_HT_CTRL_CFG 0x1410 -#define MT_RX_PARSER_CFG 0x1418 -#define MT_RX_PARSER_RX_SET_NAV_ALL BIT(0) - -#define MT_EXT_CCA_CFG 0x141c -#define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0) -#define MT_EXT_CCA_CFG_CCA1 GENMASK(3, 2) -#define MT_EXT_CCA_CFG_CCA2 GENMASK(5, 4) -#define MT_EXT_CCA_CFG_CCA3 GENMASK(7, 6) -#define MT_EXT_CCA_CFG_CCA_MASK GENMASK(11, 8) -#define MT_EXT_CCA_CFG_ED_CCA_MASK GENMASK(15, 12) - -#define MT_TX_SW_CFG3 0x1478 - -#define MT_PN_PAD_MODE 0x150c - -#define MT_TXOP_HLDR_ET 0x1608 - -#define MT_PROT_AUTO_TX_CFG 0x1648 - -#define MT_RX_STA_CNT0 0x1700 -#define MT_RX_STA_CNT1 0x1704 -#define MT_RX_STA_CNT2 0x1708 -#define MT_TX_STA_CNT0 0x170c -#define MT_TX_STA_CNT1 0x1710 -#define MT_TX_STA_CNT2 0x1714 - -/* Vendor driver defines content of the second word of STAT_FIFO as follows: - * MT_TX_STAT_FIFO_RATE GENMASK(26, 16) - * MT_TX_STAT_FIFO_ETXBF BIT(27) - * MT_TX_STAT_FIFO_SND BIT(28) - * MT_TX_STAT_FIFO_ITXBF BIT(29) - * However, tests show that b16-31 have the same layout as TXWI rate_ctl - * with rate set to rate at which frame was acked. - */ -#define MT_TX_STAT_FIFO 0x1718 -#define MT_TX_STAT_FIFO_VALID BIT(0) -#define MT_TX_STAT_FIFO_SUCCESS BIT(5) -#define MT_TX_STAT_FIFO_AGGR BIT(6) -#define MT_TX_STAT_FIFO_ACKREQ BIT(7) -#define MT_TX_STAT_FIFO_WCID GENMASK(15, 8) -#define MT_TX_STAT_FIFO_RATE GENMASK(31, 16) - -#define MT_TX_AGG_STAT 0x171c - -#define MT_TX_AGG_CNT_BASE0 0x1720 - -#define MT_MPDU_DENSITY_CNT 0x1740 - -#define MT_TX_AGG_CNT_BASE1 0x174c - -#define MT_TX_AGG_CNT(_id) ((_id) < 8 ? \ - MT_TX_AGG_CNT_BASE0 + ((_id) << 2) : \ - MT_TX_AGG_CNT_BASE1 + ((_id - 8) << 2)) - -#define MT_TX_STAT_FIFO_EXT 0x1798 -#define MT_TX_STAT_FIFO_EXT_RETRY GENMASK(7, 0) -#define MT_TX_STAT_FIFO_EXT_PKTID GENMASK(15, 8) - -#define MT_BBP_CORE_BASE 0x2000 -#define MT_BBP_IBI_BASE 0x2100 -#define MT_BBP_AGC_BASE 0x2300 -#define MT_BBP_TXC_BASE 0x2400 -#define MT_BBP_RXC_BASE 0x2500 -#define MT_BBP_TXO_BASE 0x2600 -#define MT_BBP_TXBE_BASE 0x2700 -#define MT_BBP_RXFE_BASE 0x2800 -#define MT_BBP_RXO_BASE 0x2900 -#define MT_BBP_DFS_BASE 0x2a00 -#define MT_BBP_TR_BASE 0x2b00 -#define MT_BBP_CAL_BASE 0x2c00 -#define MT_BBP_DSC_BASE 0x2e00 -#define MT_BBP_PFMU_BASE 0x2f00 - -#define MT_BBP(_type, _n) (MT_BBP_##_type##_BASE + ((_n) << 2)) - -#define MT_BBP_CORE_R1_BW GENMASK(4, 3) - -#define MT_BBP_AGC_R0_CTRL_CHAN GENMASK(9, 8) -#define MT_BBP_AGC_R0_BW GENMASK(14, 12) - -/* AGC, R4/R5 */ -#define MT_BBP_AGC_LNA_GAIN GENMASK(21, 16) - -/* AGC, R8/R9 */ -#define MT_BBP_AGC_GAIN GENMASK(14, 8) - -#define MT_BBP_AGC20_RSSI0 GENMASK(7, 0) -#define MT_BBP_AGC20_RSSI1 GENMASK(15, 8) - -#define MT_BBP_TXBE_R0_CTRL_CHAN GENMASK(1, 0) - -#define MT_WCID_ADDR_BASE 0x1800 -#define MT_WCID_ADDR(_n) (MT_WCID_ADDR_BASE + (_n) * 8) - -#define MT_SRAM_BASE 0x4000 - -#define MT_WCID_KEY_BASE 0x8000 -#define MT_WCID_KEY(_n) (MT_WCID_KEY_BASE + (_n) * 32) - -#define MT_WCID_IV_BASE 0xa000 -#define MT_WCID_IV(_n) (MT_WCID_IV_BASE + (_n) * 8) - -#define MT_WCID_ATTR_BASE 0xa800 -#define MT_WCID_ATTR(_n) (MT_WCID_ATTR_BASE + (_n) * 4) - -#define MT_WCID_ATTR_PAIRWISE BIT(0) -#define MT_WCID_ATTR_PKEY_MODE GENMASK(3, 1) -#define MT_WCID_ATTR_BSS_IDX GENMASK(6, 4) -#define MT_WCID_ATTR_RXWI_UDF GENMASK(9, 7) -#define MT_WCID_ATTR_PKEY_MODE_EXT BIT(10) -#define MT_WCID_ATTR_BSS_IDX_EXT BIT(11) -#define MT_WCID_ATTR_WAPI_MCBC BIT(15) -#define MT_WCID_ATTR_WAPI_KEYID GENMASK(31, 24) - -#define MT_SKEY_BASE_0 0xac00 -#define MT_SKEY_BASE_1 0xb400 -#define MT_SKEY_0(_bss, _idx) \ - (MT_SKEY_BASE_0 + (4 * (_bss) + _idx) * 32) -#define MT_SKEY_1(_bss, _idx) \ - (MT_SKEY_BASE_1 + (4 * ((_bss) & 7) + _idx) * 32) -#define MT_SKEY(_bss, _idx) \ - ((_bss & 8) ? MT_SKEY_1(_bss, _idx) : MT_SKEY_0(_bss, _idx)) - -#define MT_SKEY_MODE_BASE_0 0xb000 -#define MT_SKEY_MODE_BASE_1 0xb3f0 -#define MT_SKEY_MODE_0(_bss) \ - (MT_SKEY_MODE_BASE_0 + ((_bss / 2) << 2)) -#define MT_SKEY_MODE_1(_bss) \ - (MT_SKEY_MODE_BASE_1 + ((((_bss) & 7) / 2) << 2)) -#define MT_SKEY_MODE(_bss) \ - ((_bss & 8) ? MT_SKEY_MODE_1(_bss) : MT_SKEY_MODE_0(_bss)) -#define MT_SKEY_MODE_MASK GENMASK(3, 0) -#define MT_SKEY_MODE_SHIFT(_bss, _idx) (4 * ((_idx) + 4 * (_bss & 1))) - -#define MT_BEACON_BASE 0xc000 - -#define MT_TEMP_SENSOR 0x1d000 -#define MT_TEMP_SENSOR_VAL GENMASK(6, 0) - -enum mt76_cipher_type { - MT_CIPHER_NONE, - MT_CIPHER_WEP40, - MT_CIPHER_WEP104, - MT_CIPHER_TKIP, - MT_CIPHER_AES_CCMP, - MT_CIPHER_CKIP40, - MT_CIPHER_CKIP104, - MT_CIPHER_CKIP128, - MT_CIPHER_WAPI, -}; - -#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/trace.h b/drivers/net/wireless/mediatek/mt76/mt76x0/trace.h index 8a752a09f2dc..36bbdd585163 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/trace.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/trace.h @@ -178,11 +178,11 @@ DECLARE_EVENT_CLASS(dev_simple_evt, ); TRACE_EVENT(mt76x0_rx, - TP_PROTO(struct mt76_dev *dev, struct mt76x0_rxwi *rxwi, u32 f), + TP_PROTO(struct mt76_dev *dev, struct mt76x02_rxwi *rxwi, u32 f), TP_ARGS(dev, rxwi, f), TP_STRUCT__entry( DEV_ENTRY - __field_struct(struct mt76x0_rxwi, rxwi) + __field_struct(struct mt76x02_rxwi, rxwi) __field(u32, fce_info) ), TP_fast_assign( @@ -197,13 +197,13 @@ TRACE_EVENT(mt76x0_rx, TRACE_EVENT(mt76x0_tx, TP_PROTO(struct mt76_dev *dev, struct sk_buff *skb, - struct mt76_sta *sta, struct mt76_txwi *h), + struct mt76x02_sta *sta, struct mt76x02_txwi *h), TP_ARGS(dev, skb, sta, h), TP_STRUCT__entry( DEV_ENTRY - __field_struct(struct mt76_txwi, h) + __field_struct(struct mt76x02_txwi, h) __field(struct sk_buff *, skb) - __field(struct mt76_sta *, sta) + __field(struct mt76x02_sta *, sta) ), TP_fast_assign( DEV_ASSIGN; @@ -211,11 +211,11 @@ TRACE_EVENT(mt76x0_tx, __entry->skb = skb; __entry->sta = sta; ), - TP_printk(DEV_PR_FMT "skb:%p sta:%p flg:%04hx rate_ctl:%04hx " + TP_printk(DEV_PR_FMT "skb:%p sta:%p flg:%04hx rate:%04hx " "ack:%02hhx wcid:%02hhx len_ctl:%05hx", DEV_PR_ARG, __entry->skb, __entry->sta, le16_to_cpu(__entry->h.flags), - le16_to_cpu(__entry->h.rate_ctl), + le16_to_cpu(__entry->h.rate), __entry->h.ack_ctl, __entry->h.wcid, le16_to_cpu(__entry->h.len_ctl)) ); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c b/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c index 751b49c28ae5..c6d8ba01feb1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/tx.c @@ -14,74 +14,22 @@ #include "mt76x0.h" #include "trace.h" +#include "../mt76x02_util.h" +#include "../mt76x02_usb.h" -/* Take mac80211 Q id from the skb and translate it to hardware Q id */ -static u8 skb2q(struct sk_buff *skb) -{ - int qid = skb_get_queue_mapping(skb); - - if (WARN_ON(qid >= MT_TXQ_PSD)) { - qid = MT_TXQ_BE; - skb_set_queue_mapping(skb, qid); - } - - return q2hwq(qid); -} - -static void mt76x0_tx_skb_remove_dma_overhead(struct sk_buff *skb, - struct ieee80211_tx_info *info) -{ - int pkt_len = (unsigned long)info->status.status_driver_data[0]; - - skb_pull(skb, sizeof(struct mt76_txwi) + 4); - if (ieee80211_get_hdrlen_from_skb(skb) % 4) - mt76x0_remove_hdr_pad(skb); - - skb_trim(skb, pkt_len); -} - -void mt76x0_tx_status(struct mt76x0_dev *dev, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - mt76x0_tx_skb_remove_dma_overhead(skb, info); - - ieee80211_tx_info_clear_status(info); - info->status.rates[0].idx = -1; - info->flags |= IEEE80211_TX_STAT_ACK; - - spin_lock(&dev->mac_lock); - ieee80211_tx_status(dev->mt76.hw, skb); - spin_unlock(&dev->mac_lock); -} - -static int mt76x0_skb_rooms(struct mt76x0_dev *dev, struct sk_buff *skb) -{ - int hdr_len = ieee80211_get_hdrlen_from_skb(skb); - u32 need_head; - - need_head = sizeof(struct mt76_txwi) + 4; - if (hdr_len % 4) - need_head += 2; - - return skb_cow(skb, need_head); -} - -static struct mt76_txwi * +static struct mt76x02_txwi * mt76x0_push_txwi(struct mt76x0_dev *dev, struct sk_buff *skb, struct ieee80211_sta *sta, struct mt76_wcid *wcid, int pkt_len) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rate = &info->control.rates[0]; - struct mt76_txwi *txwi; + struct mt76x02_txwi *txwi; unsigned long flags; - u16 txwi_flags = 0; - u32 pkt_id; u16 rate_ctl; u8 nss; - txwi = (struct mt76_txwi *)skb_push(skb, sizeof(struct mt76_txwi)); + txwi = (struct mt76x02_txwi *)skb_push(skb, sizeof(struct mt76x02_txwi)); memset(txwi, 0, sizeof(*txwi)); if (!wcid->tx_rate_set) @@ -93,178 +41,75 @@ mt76x0_push_txwi(struct mt76x0_dev *dev, struct sk_buff *skb, rate_ctl = wcid->tx_rate; nss = wcid->tx_rate_nss; } else { - rate_ctl = mt76x0_mac_tx_rate_val(dev, rate, &nss); + rate_ctl = mt76x02_mac_tx_rate_val(&dev->mt76, rate, &nss); } spin_unlock_irqrestore(&dev->mt76.lock, flags); - txwi->rate_ctl = cpu_to_le16(rate_ctl); - - if (info->flags & IEEE80211_TX_CTL_LDPC) - txwi->rate_ctl |= cpu_to_le16(MT_RXWI_RATE_LDPC); - if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1) - txwi->rate_ctl |= cpu_to_le16(MT_RXWI_RATE_STBC); - if (nss > 1 && sta && sta->smps_mode == IEEE80211_SMPS_DYNAMIC) - txwi_flags |= MT_TXWI_FLAGS_MMPS; - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ; - pkt_id = 1; - } else { - pkt_id = 0; - } - - if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) - pkt_id |= MT_TXWI_PKTID_PROBE; - - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ; - - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) { - u8 ba_size = IEEE80211_MIN_AMPDU_BUF; - - ba_size <<= sta->ht_cap.ampdu_factor; - ba_size = min_t(int, 7, ba_size - 1); - if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { - ba_size = 0; - } else { - txwi_flags |= MT_TXWI_FLAGS_AMPDU; - txwi_flags |= FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, - sta->ht_cap.ampdu_density); - } - txwi->ack_ctl |= FIELD_PREP(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size); - } - txwi->wcid = wcid->idx; - txwi->flags |= cpu_to_le16(txwi_flags); - txwi->len_ctl = cpu_to_le16(pkt_len); - txwi->pktid = pkt_id; + txwi->rate = cpu_to_le16(rate_ctl); + txwi->pktid = (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) ? 1 : 0; + + mt76x02_mac_fill_txwi(txwi, skb, sta, pkt_len, nss); return txwi; } void mt76x0_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, - struct sk_buff *skb) + struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct mt76x0_dev *dev = hw->priv; struct ieee80211_vif *vif = info->control.vif; - struct ieee80211_sta *sta = control->sta; - struct mt76_sta *msta = NULL; - struct mt76_wcid *wcid = dev->mon_wcid; - struct mt76_txwi *txwi; - int pkt_len = skb->len; - int hw_q = skb2q(skb); + struct mt76_wcid *wcid = &dev->mt76.global_wcid; - BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); - info->status.status_driver_data[0] = (void *)(unsigned long)pkt_len; + if (control->sta) { + struct mt76x02_sta *msta; - if (mt76x0_skb_rooms(dev, skb) || mt76x0_insert_hdr_pad(skb)) { - ieee80211_free_txskb(dev->mt76.hw, skb); - return; + msta = (struct mt76x02_sta *)control->sta->drv_priv; + wcid = &msta->wcid; + /* sw encrypted frames */ + if (!info->control.hw_key && wcid->hw_key_idx != 0xff) + control->sta = NULL; } - if (sta) { - msta = (struct mt76_sta *) sta->drv_priv; - wcid = &msta->wcid; - } else if (vif && (!info->control.hw_key && wcid->hw_key_idx != -1)) { - struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + if (vif && !control->sta) { + struct mt76x02_vif *mvif; + mvif = (struct mt76x02_vif *)vif->drv_priv; wcid = &mvif->group_wcid; } - txwi = mt76x0_push_txwi(dev, skb, sta, wcid, pkt_len); - - if (mt76x0_dma_enqueue_tx(dev, skb, wcid, hw_q)) - return; - - trace_mt76x0_tx(&dev->mt76, skb, msta, txwi); + mt76_tx(&dev->mt76, control->sta, wcid, skb); } -void mt76x0_tx_stat(struct work_struct *work) +int mt76x0_tx_prepare_skb(struct mt76_dev *mdev, void *data, + struct sk_buff *skb, struct mt76_queue *q, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + u32 *tx_info) { - struct mt76x0_dev *dev = container_of(work, struct mt76x0_dev, - stat_work.work); - struct mt76_tx_status stat; - unsigned long flags; - int cleaned = 0; - u8 update = 1; + struct mt76x0_dev *dev = container_of(mdev, struct mt76x0_dev, mt76); + struct mt76x02_txwi *txwi; + int len = skb->len; - while (!test_bit(MT76_REMOVED, &dev->mt76.state)) { - stat = mt76x0_mac_fetch_tx_status(dev); - if (!stat.valid) - break; + mt76x02_insert_hdr_pad(skb); + txwi = mt76x0_push_txwi(dev, skb, sta, wcid, len); - mt76x0_send_tx_status(dev, &stat, &update); - - cleaned++; - } - trace_mt76x0_tx_status_cleaned(&dev->mt76, cleaned); - - spin_lock_irqsave(&dev->tx_lock, flags); - if (cleaned) - queue_delayed_work(dev->stat_wq, &dev->stat_work, - msecs_to_jiffies(10)); - else if (test_and_clear_bit(MT76_MORE_STATS, &dev->mt76.state)) - queue_delayed_work(dev->stat_wq, &dev->stat_work, - msecs_to_jiffies(20)); - else - clear_bit(MT76_READING_STATS, &dev->mt76.state); - spin_unlock_irqrestore(&dev->tx_lock, flags); + return mt76x02u_set_txinfo(skb, wcid, q2ep(q->hw_idx)); } +EXPORT_SYMBOL_GPL(mt76x0_tx_prepare_skb); -int mt76x0_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u16 queue, const struct ieee80211_tx_queue_params *params) +void mt76x0_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb) { - struct mt76x0_dev *dev = hw->priv; - u8 cw_min = 5, cw_max = 10, hw_q = q2hwq(queue); - u32 val; - - /* TODO: should we do funny things with the parameters? - * See what mt76x0_set_default_edca() used to do in init.c. - */ - - if (params->cw_min) - cw_min = fls(params->cw_min); - if (params->cw_max) - cw_max = fls(params->cw_max); - - WARN_ON(params->txop > 0xff); - WARN_ON(params->aifs > 0xf); - WARN_ON(cw_min > 0xf); - WARN_ON(cw_max > 0xf); + struct mt76x0_dev *dev = container_of(mdev, struct mt76x0_dev, mt76); + void *rxwi = skb->data; - val = FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) | - FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) | - FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max); - /* TODO: based on user-controlled EnableTxBurst var vendor drv sets - * a really long txop on AC0 (see connect.c:2009) but only on - * connect? When not connected should be 0. - */ - if (!hw_q) - val |= 0x60; - else - val |= FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop); - mt76_wr(dev, MT_EDCA_CFG_AC(hw_q), val); - - val = mt76_rr(dev, MT_WMM_TXOP(hw_q)); - val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(hw_q)); - val |= params->txop << MT_WMM_TXOP_SHIFT(hw_q); - mt76_wr(dev, MT_WMM_TXOP(hw_q), val); - - val = mt76_rr(dev, MT_WMM_AIFSN); - val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(hw_q)); - val |= params->aifs << MT_WMM_AIFSN_SHIFT(hw_q); - mt76_wr(dev, MT_WMM_AIFSN, val); - - val = mt76_rr(dev, MT_WMM_CWMIN); - val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(hw_q)); - val |= cw_min << MT_WMM_CWMIN_SHIFT(hw_q); - mt76_wr(dev, MT_WMM_CWMIN, val); - - val = mt76_rr(dev, MT_WMM_CWMAX); - val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(hw_q)); - val |= cw_max << MT_WMM_CWMAX_SHIFT(hw_q); - mt76_wr(dev, MT_WMM_CWMAX, val); + skb_pull(skb, sizeof(struct mt76x02_rxwi)); + if (!mt76x0_mac_process_rx(dev, skb, rxwi)) { + dev_kfree_skb(skb); + return; + } - return 0; + mt76_rx(&dev->mt76, q, skb); } +EXPORT_SYMBOL_GPL(mt76x0_queue_rx_skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 54ae1f113be2..bb8c0cd3d48a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -13,11 +13,16 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/firmware.h> #include <linux/usb.h> #include "mt76x0.h" -#include "usb.h" +#include "mcu.h" #include "trace.h" +#include "../mt76x02_util.h" +#include "../mt76x02_usb.h" + +#define MT7610U_FIRMWARE "mediatek/mt7610u.bin" static struct usb_device_id mt76x0_device_table[] = { { USB_DEVICE(0x148F, 0x7610) }, /* MT7610U */ @@ -46,230 +51,178 @@ static struct usb_device_id mt76x0_device_table[] = { { 0, } }; -bool mt76x0_usb_alloc_buf(struct mt76x0_dev *dev, size_t len, - struct mt76x0_dma_buf *buf) -{ - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - - buf->len = len; - buf->urb = usb_alloc_urb(0, GFP_KERNEL); - buf->buf = usb_alloc_coherent(usb_dev, buf->len, GFP_KERNEL, &buf->dma); - - return !buf->urb || !buf->buf; -} +#define MCU_FW_URB_MAX_PAYLOAD 0x38f8 +#define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12) -void mt76x0_usb_free_buf(struct mt76x0_dev *dev, struct mt76x0_dma_buf *buf) +static inline int mt76x0_firmware_running(struct mt76x0_dev *dev) { - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - - usb_free_coherent(usb_dev, buf->len, buf->buf, buf->dma); - usb_free_urb(buf->urb); + return mt76_rr(dev, MT_MCU_COM_REG0) == 1; } -int mt76x0_usb_submit_buf(struct mt76x0_dev *dev, int dir, int ep_idx, - struct mt76x0_dma_buf *buf, gfp_t gfp, - usb_complete_t complete_fn, void *context) +static int +mt76x0u_upload_firmware(struct mt76x0_dev *dev, + const struct mt76x02_fw_header *hdr) { - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - unsigned pipe; - int ret; - - if (dir == USB_DIR_IN) - pipe = usb_rcvbulkpipe(usb_dev, dev->in_ep[ep_idx]); - else - pipe = usb_sndbulkpipe(usb_dev, dev->out_ep[ep_idx]); + u8 *fw_payload = (u8 *)(hdr + 1); + u32 ilm_len, dlm_len; + void *ivb; + int err; - usb_fill_bulk_urb(buf->urb, usb_dev, pipe, buf->buf, buf->len, - complete_fn, context); - buf->urb->transfer_dma = buf->dma; - buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - trace_mt76x0_submit_urb(&dev->mt76, buf->urb); - ret = usb_submit_urb(buf->urb, gfp); - if (ret) - dev_err(dev->mt76.dev, "Error: submit URB dir:%d ep:%d failed:%d\n", - dir, ep_idx, ret); - return ret; -} - -void mt76x0_complete_urb(struct urb *urb) -{ - struct completion *cmpl = urb->context; - - complete(cmpl); -} + ivb = kmemdup(fw_payload, MT_MCU_IVB_SIZE, GFP_KERNEL); + if (!ivb) + return -ENOMEM; -int mt76x0_vendor_request(struct mt76x0_dev *dev, const u8 req, - const u8 direction, const u16 val, const u16 offset, - void *buf, const size_t buflen) -{ - int i, ret; - struct usb_device *usb_dev = mt76x0_to_usb_dev(dev); - const u8 req_type = direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE; - const unsigned int pipe = (direction == USB_DIR_IN) ? - usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); - - for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { - ret = usb_control_msg(usb_dev, pipe, req, req_type, - val, offset, buf, buflen, - MT_VEND_REQ_TOUT_MS); - trace_mt76x0_vend_req(&dev->mt76, pipe, req, req_type, val, offset, - buf, buflen, ret); - - if (ret == -ENODEV) - set_bit(MT76_REMOVED, &dev->mt76.state); - if (ret >= 0 || ret == -ENODEV) - return ret; - - msleep(5); + ilm_len = le32_to_cpu(hdr->ilm_len) - MT_MCU_IVB_SIZE; + dev_dbg(dev->mt76.dev, "loading FW - ILM %u + IVB %u\n", + ilm_len, MT_MCU_IVB_SIZE); + err = mt76x02u_mcu_fw_send_data(&dev->mt76, + fw_payload + MT_MCU_IVB_SIZE, + ilm_len, MCU_FW_URB_MAX_PAYLOAD, + MT_MCU_IVB_SIZE); + if (err) + goto out; + + dlm_len = le32_to_cpu(hdr->dlm_len); + dev_dbg(dev->mt76.dev, "loading FW - DLM %u\n", dlm_len); + err = mt76x02u_mcu_fw_send_data(&dev->mt76, + fw_payload + le32_to_cpu(hdr->ilm_len), + dlm_len, MCU_FW_URB_MAX_PAYLOAD, + MT_MCU_DLM_OFFSET); + if (err) + goto out; + + err = mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x12, 0, ivb, MT_MCU_IVB_SIZE); + if (err < 0) + goto out; + + if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 1000)) { + dev_err(dev->mt76.dev, "Firmware failed to start\n"); + err = -ETIMEDOUT; + goto out; } - dev_err(dev->mt76.dev, "Vendor request req:%02x off:%04x failed:%d\n", - req, offset, ret); + dev_dbg(dev->mt76.dev, "Firmware running!\n"); - return ret; -} +out: + kfree(ivb); -void mt76x0_vendor_reset(struct mt76x0_dev *dev) -{ - mt76x0_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, - MT_VEND_DEV_MODE_RESET, 0, NULL, 0); + return err; } -static u32 mt76x0_rr(struct mt76_dev *dev, u32 offset) +static int mt76x0u_load_firmware(struct mt76x0_dev *dev) { - struct mt76x0_dev *mdev = (struct mt76x0_dev *) dev; - int ret; - u32 val = ~0; - - WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset); - - mutex_lock(&mdev->usb_ctrl_mtx); + const struct firmware *fw; + const struct mt76x02_fw_header *hdr; + int len, ret; + u32 val; - ret = mt76x0_vendor_request((struct mt76x0_dev *)dev, MT_VEND_MULTI_READ, USB_DIR_IN, - 0, offset, mdev->data, MT_VEND_BUF); - if (ret == MT_VEND_BUF) - val = get_unaligned_le32(mdev->data); - else if (ret > 0) - dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n", - ret, offset); + mt76_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN)); - mutex_unlock(&mdev->usb_ctrl_mtx); - - trace_mt76x0_reg_read(dev, offset, val); - return val; -} - -int mt76x0_vendor_single_wr(struct mt76x0_dev *dev, const u8 req, - const u16 offset, const u32 val) -{ - struct mt76x0_dev *mdev = dev; - int ret; + if (mt76x0_firmware_running(dev)) + return 0; - mutex_lock(&mdev->usb_ctrl_mtx); + ret = request_firmware(&fw, MT7610U_FIRMWARE, dev->mt76.dev); + if (ret) + return ret; - ret = mt76x0_vendor_request(dev, req, USB_DIR_OUT, - val & 0xffff, offset, NULL, 0); - if (!ret) - ret = mt76x0_vendor_request(dev, req, USB_DIR_OUT, - val >> 16, offset + 2, NULL, 0); + if (!fw || !fw->data || fw->size < sizeof(*hdr)) + goto err_inv_fw; - mutex_unlock(&mdev->usb_ctrl_mtx); + hdr = (const struct mt76x02_fw_header *)fw->data; - return ret; -} + if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) + goto err_inv_fw; -static void mt76x0_wr(struct mt76_dev *dev, u32 offset, u32 val) -{ - struct mt76x0_dev *mdev = (struct mt76x0_dev *) dev; - int ret; + len = sizeof(*hdr); + len += le32_to_cpu(hdr->ilm_len); + len += le32_to_cpu(hdr->dlm_len); - WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset); + if (fw->size != len) + goto err_inv_fw; - mutex_lock(&mdev->usb_ctrl_mtx); + val = le16_to_cpu(hdr->fw_ver); + dev_dbg(dev->mt76.dev, + "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, + le16_to_cpu(hdr->build_ver), hdr->build_time); - put_unaligned_le32(val, mdev->data); - ret = mt76x0_vendor_request(mdev, MT_VEND_MULTI_WRITE, USB_DIR_OUT, - 0, offset, mdev->data, MT_VEND_BUF); - trace_mt76x0_reg_write(dev, offset, val); + len = le32_to_cpu(hdr->ilm_len); - mutex_unlock(&mdev->usb_ctrl_mtx); -} + mt76_wr(dev, 0x1004, 0x2c); -static u32 mt76x0_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val) -{ - val |= mt76x0_rr(dev, offset) & ~mask; - mt76x0_wr(dev, offset, val); - return val; -} + mt76_set(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN) | + FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20)); + mt76x02u_mcu_fw_reset(&dev->mt76); + msleep(5); +/* + mt76x0_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | + MT_PBF_CFG_TX1Q_EN | + MT_PBF_CFG_TX2Q_EN | + MT_PBF_CFG_TX3Q_EN)); +*/ + + mt76_wr(dev, MT_FCE_PSE_CTRL, 1); + + /* FCE tx_fs_base_ptr */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); + /* FCE tx_fs_max_cnt */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); + /* FCE pdma enable */ + mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); + /* FCE skip_fs_en */ + mt76_wr(dev, MT_FCE_SKIP_FS, 3); + + val = mt76_rr(dev, MT_USB_DMA_CFG); + val |= MT_USB_DMA_CFG_UDMA_TX_WL_DROP; + mt76_wr(dev, MT_USB_DMA_CFG, val); + val &= ~MT_USB_DMA_CFG_UDMA_TX_WL_DROP; + mt76_wr(dev, MT_USB_DMA_CFG, val); + + ret = mt76x0u_upload_firmware(dev, hdr); + release_firmware(fw); + + mt76_wr(dev, MT_FCE_PSE_CTRL, 1); -static void mt76x0_wr_copy(struct mt76_dev *dev, u32 offset, - const void *data, int len) -{ - WARN_ONCE(offset & 3, "unaligned write copy off:%08x", offset); - WARN_ONCE(len & 3, "short write copy off:%08x", offset); + return ret; - mt76x0_burst_write_regs((struct mt76x0_dev *) dev, offset, data, len / 4); +err_inv_fw: + dev_err(dev->mt76.dev, "Invalid firmware image\n"); + release_firmware(fw); + return -ENOENT; } -void mt76x0_addr_wr(struct mt76x0_dev *dev, const u32 offset, const u8 *addr) +static int mt76x0u_mcu_init(struct mt76x0_dev *dev) { - mt76_wr(dev, offset, get_unaligned_le32(addr)); - mt76_wr(dev, offset + 4, addr[4] | addr[5] << 8); -} + int ret; -static int mt76x0_assign_pipes(struct usb_interface *usb_intf, - struct mt76x0_dev *dev) -{ - struct usb_endpoint_descriptor *ep_desc; - struct usb_host_interface *intf_desc = usb_intf->cur_altsetting; - unsigned i, ep_i = 0, ep_o = 0; - - BUILD_BUG_ON(sizeof(dev->in_ep) < __MT_EP_IN_MAX); - BUILD_BUG_ON(sizeof(dev->out_ep) < __MT_EP_OUT_MAX); - - for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { - ep_desc = &intf_desc->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep_desc) && - ep_i++ < __MT_EP_IN_MAX) { - dev->in_ep[ep_i - 1] = usb_endpoint_num(ep_desc); - dev->in_max_packet = usb_endpoint_maxp(ep_desc); - /* Note: this is ignored by usb sub-system but vendor - * code does it. We can drop this at some point. - */ - dev->in_ep[ep_i - 1] |= USB_DIR_IN; - } else if (usb_endpoint_is_bulk_out(ep_desc) && - ep_o++ < __MT_EP_OUT_MAX) { - dev->out_ep[ep_o - 1] = usb_endpoint_num(ep_desc); - dev->out_max_packet = usb_endpoint_maxp(ep_desc); - } - } + ret = mt76x0u_load_firmware(dev); + if (ret < 0) + return ret; - if (ep_i != __MT_EP_IN_MAX || ep_o != __MT_EP_OUT_MAX) { - dev_err(dev->mt76.dev, "Error: wrong pipe number in:%d out:%d\n", - ep_i, ep_o); - return -EINVAL; - } + set_bit(MT76_STATE_MCU_RUNNING, &dev->mt76.state); return 0; } -static int mt76x0_probe(struct usb_interface *usb_intf, +static int mt76x0u_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { + static const struct mt76_driver_ops drv_ops = { + .tx_prepare_skb = mt76x0_tx_prepare_skb, + .tx_complete_skb = mt76x02_tx_complete_skb, + .tx_status_data = mt76x02_tx_status_data, + .rx_skb = mt76x0_queue_rx_skb, + }; struct usb_device *usb_dev = interface_to_usbdev(usb_intf); struct mt76x0_dev *dev; u32 asic_rev, mac_rev; int ret; - static const struct mt76_bus_ops usb_ops = { - .rr = mt76x0_rr, - .wr = mt76x0_wr, - .rmw = mt76x0_rmw, - .copy = mt76x0_wr_copy, - }; - dev = mt76x0_alloc_device(&usb_intf->dev); + dev = mt76x0_alloc_device(&usb_intf->dev, &drv_ops); if (!dev) return -ENOMEM; @@ -278,18 +231,18 @@ static int mt76x0_probe(struct usb_interface *usb_intf, usb_set_intfdata(usb_intf, dev); - dev->mt76.bus = &usb_ops; - - ret = mt76x0_assign_pipes(usb_intf, dev); + mt76x02u_init_mcu(&dev->mt76); + ret = mt76u_init(&dev->mt76, usb_intf); if (ret) goto err; /* Disable the HW, otherwise MCU fail to initalize on hot reboot */ mt76x0_chip_onoff(dev, false, false); - ret = mt76x0_wait_asic_ready(dev); - if (ret) + if (!mt76x02_wait_for_mac(&dev->mt76)) { + ret = -ETIMEDOUT; goto err; + } asic_rev = mt76_rr(dev, MT_ASIC_VERSION); mac_rev = mt76_rr(dev, MT_MAC_CSR0); @@ -300,10 +253,23 @@ static int mt76x0_probe(struct usb_interface *usb_intf, if (!(mt76_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL)) dev_warn(dev->mt76.dev, "Warning: eFUSE not present\n"); - ret = mt76x0_init_hardware(dev); - if (ret) + ret = mt76u_mcu_init_rx(&dev->mt76); + if (ret < 0) + goto err; + + ret = mt76u_alloc_queues(&dev->mt76); + if (ret < 0) goto err; + mt76x0_chip_onoff(dev, true, true); + + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + ret = mt76x0u_mcu_init(dev); + if (ret) + goto err_hw; + ret = mt76x0_register_device(dev); if (ret) goto err_hw; @@ -317,7 +283,6 @@ err: usb_set_intfdata(usb_intf, NULL); usb_put_dev(interface_to_usbdev(usb_intf)); - destroy_workqueue(dev->stat_wq); ieee80211_free_hw(dev->mt76.hw); return ret; } @@ -336,41 +301,62 @@ static void mt76x0_disconnect(struct usb_interface *usb_intf) usb_set_intfdata(usb_intf, NULL); usb_put_dev(interface_to_usbdev(usb_intf)); - destroy_workqueue(dev->stat_wq); ieee80211_free_hw(dev->mt76.hw); } -static int mt76x0_suspend(struct usb_interface *usb_intf, pm_message_t state) +static int __maybe_unused mt76x0_suspend(struct usb_interface *usb_intf, + pm_message_t state) { struct mt76x0_dev *dev = usb_get_intfdata(usb_intf); + struct mt76_usb *usb = &dev->mt76.usb; - mt76x0_cleanup(dev); + mt76u_stop_queues(&dev->mt76); + mt76x0_mac_stop(dev); + usb_kill_urb(usb->mcu.res.urb); return 0; } -static int mt76x0_resume(struct usb_interface *usb_intf) +static int __maybe_unused mt76x0_resume(struct usb_interface *usb_intf) { struct mt76x0_dev *dev = usb_get_intfdata(usb_intf); + struct mt76_usb *usb = &dev->mt76.usb; int ret; + reinit_completion(&usb->mcu.cmpl); + ret = mt76u_submit_buf(&dev->mt76, USB_DIR_IN, + MT_EP_IN_CMD_RESP, + &usb->mcu.res, GFP_KERNEL, + mt76u_mcu_complete_urb, + &usb->mcu.cmpl); + if (ret < 0) + goto err; + + ret = mt76u_submit_rx_buffers(&dev->mt76); + if (ret < 0) + goto err; + + tasklet_enable(&usb->rx_tasklet); + tasklet_enable(&usb->tx_tasklet); + ret = mt76x0_init_hardware(dev); if (ret) - return ret; - - set_bit(MT76_STATE_INITIALIZED, &dev->mt76.state); + goto err; return 0; +err: + mt76x0_cleanup(dev); + return ret; } MODULE_DEVICE_TABLE(usb, mt76x0_device_table); -MODULE_FIRMWARE(MT7610_FIRMWARE); +MODULE_FIRMWARE(MT7610U_FIRMWARE); MODULE_LICENSE("GPL"); static struct usb_driver mt76x0_driver = { .name = KBUILD_MODNAME, .id_table = mt76x0_device_table, - .probe = mt76x0_probe, + .probe = mt76x0u_probe, .disconnect = mt76x0_disconnect, .suspend = mt76x0_suspend, .resume = mt76x0_resume, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.h b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.h deleted file mode 100644 index 492e431390a8..000000000000 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * 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. - */ - -#ifndef __MT76X0U_USB_H -#define __MT76X0U_USB_H - -#include "mt76x0.h" - -#define MT7610_FIRMWARE "mediatek/mt7610u.bin" - -#define MT_VEND_REQ_MAX_RETRY 10 -#define MT_VEND_REQ_TOUT_MS 300 - -#define MT_VEND_DEV_MODE_RESET 1 - -#define MT_VEND_BUF sizeof(__le32) - -static inline struct usb_device *mt76x0_to_usb_dev(struct mt76x0_dev *mt76x0) -{ - return interface_to_usbdev(to_usb_interface(mt76x0->mt76.dev)); -} - -static inline struct usb_device *mt76_to_usb_dev(struct mt76_dev *mt76) -{ - return interface_to_usbdev(to_usb_interface(mt76->dev)); -} - -static inline bool mt76x0_urb_has_error(struct urb *urb) -{ - return urb->status && - urb->status != -ENOENT && - urb->status != -ECONNRESET && - urb->status != -ESHUTDOWN; -} - -bool mt76x0_usb_alloc_buf(struct mt76x0_dev *dev, size_t len, - struct mt76x0_dma_buf *buf); -void mt76x0_usb_free_buf(struct mt76x0_dev *dev, struct mt76x0_dma_buf *buf); -int mt76x0_usb_submit_buf(struct mt76x0_dev *dev, int dir, int ep_idx, - struct mt76x0_dma_buf *buf, gfp_t gfp, - usb_complete_t complete_fn, void *context); -void mt76x0_complete_urb(struct urb *urb); - -int mt76x0_vendor_request(struct mt76x0_dev *dev, const u8 req, - const u8 direction, const u16 val, const u16 offset, - void *buf, const size_t buflen); -void mt76x0_vendor_reset(struct mt76x0_dev *dev); -int mt76x0_vendor_single_wr(struct mt76x0_dev *dev, const u8 req, - const u16 offset, const u32 val); - -#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/util.c b/drivers/net/wireless/mediatek/mt76/mt76x0/util.c deleted file mode 100644 index 7856dd760419..000000000000 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/util.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * 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. - */ - -#include "mt76x0.h" - -void mt76x0_remove_hdr_pad(struct sk_buff *skb) -{ - int len = ieee80211_get_hdrlen_from_skb(skb); - - memmove(skb->data + 2, skb->data, len); - skb_pull(skb, 2); -} - -int mt76x0_insert_hdr_pad(struct sk_buff *skb) -{ - int len = ieee80211_get_hdrlen_from_skb(skb); - int ret; - - if (len % 4 == 0) - return 0; - - ret = skb_cow(skb, 2); - if (ret) - return ret; - - skb_push(skb, 2); - memmove(skb->data, skb->data + 2, len); - - skb->data[len] = 0; - skb->data[len + 1] = 0; - return 0; -} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h b/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h new file mode 100644 index 000000000000..32a323ebc6a7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * 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 __MT76x02_DMA_H +#define __MT76x02_DMA_H + +#include "dma.h" + +#define MT_TXD_INFO_LEN GENMASK(15, 0) +#define MT_TXD_INFO_NEXT_VLD BIT(16) +#define MT_TXD_INFO_TX_BURST BIT(17) +#define MT_TXD_INFO_80211 BIT(19) +#define MT_TXD_INFO_TSO BIT(20) +#define MT_TXD_INFO_CSO BIT(21) +#define MT_TXD_INFO_WIV BIT(24) +#define MT_TXD_INFO_QSEL GENMASK(26, 25) +#define MT_TXD_INFO_DPORT GENMASK(29, 27) +#define MT_TXD_INFO_TYPE GENMASK(31, 30) + +#define MT_RX_FCE_INFO_LEN GENMASK(13, 0) +#define MT_RX_FCE_INFO_SELF_GEN BIT(15) +#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16) +#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20) +#define MT_RX_FCE_INFO_PCIE_INTR BIT(24) +#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25) +#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27) +#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30) + +/* MCU request message header */ +#define MT_MCU_MSG_LEN GENMASK(15, 0) +#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16) +#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20) +#define MT_MCU_MSG_PORT GENMASK(29, 27) +#define MT_MCU_MSG_TYPE GENMASK(31, 30) +#define MT_MCU_MSG_TYPE_CMD BIT(30) + +enum dma_msg_port { + WLAN_PORT, + CPU_RX_PORT, + CPU_TX_PORT, + HOST_PORT, + VIRTUAL_CPU_RX_PORT, + VIRTUAL_CPU_TX_PORT, + DISCARD, +}; + +#endif /* __MT76x02_DMA_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c new file mode 100644 index 000000000000..0b12299c7a41 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> + * + * 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" +#include "mt76x02_regs.h" +#include "mt76x02_mac.h" + +enum mt76x02_cipher_type +mt76x02_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) +{ + memset(key_data, 0, 32); + if (!key) + return MT_CIPHER_NONE; + + if (key->keylen > 32) + return MT_CIPHER_NONE; + + memcpy(key_data, key->key, key->keylen); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MT_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return MT_CIPHER_AES_CCMP; + default: + return MT_CIPHER_NONE; + } +} +EXPORT_SYMBOL_GPL(mt76x02_mac_get_key_info); + +int mt76x02_mac_shared_key_setup(struct mt76_dev *dev, u8 vif_idx, u8 key_idx, + struct ieee80211_key_conf *key) +{ + enum mt76x02_cipher_type cipher; + u8 key_data[32]; + u32 val; + + cipher = mt76x02_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EOPNOTSUPP; + + val = __mt76_rr(dev, MT_SKEY_MODE(vif_idx)); + val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); + val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); + __mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); + + __mt76_wr_copy(dev, MT_SKEY(vif_idx, key_idx), key_data, + sizeof(key_data)); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mac_shared_key_setup); + +int mt76x02_mac_wcid_set_key(struct mt76_dev *dev, u8 idx, + struct ieee80211_key_conf *key) +{ + enum mt76x02_cipher_type cipher; + u8 key_data[32]; + u8 iv_data[8]; + + cipher = mt76x02_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EOPNOTSUPP; + + __mt76_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); + __mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PKEY_MODE, cipher); + + memset(iv_data, 0, sizeof(iv_data)); + if (key) { + __mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE, + !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); + iv_data[3] = key->keyidx << 6; + if (cipher >= MT_CIPHER_TKIP) + iv_data[3] |= 0x20; + } + + __mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mac_wcid_set_key); + +void mt76x02_mac_wcid_setup(struct mt76_dev *dev, u8 idx, u8 vif_idx, u8 *mac) +{ + struct mt76_wcid_addr addr = {}; + u32 attr; + + attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | + FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); + + __mt76_wr(dev, MT_WCID_ATTR(idx), attr); + + __mt76_wr(dev, MT_WCID_TX_RATE(idx), 0); + __mt76_wr(dev, MT_WCID_TX_RATE(idx) + 4, 0); + + if (idx >= 128) + return; + + if (mac) + memcpy(addr.macaddr, mac, ETH_ALEN); + + __mt76_wr_copy(dev, MT_WCID_ADDR(idx), &addr, sizeof(addr)); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_wcid_setup); + +void mt76x02_mac_wcid_set_drop(struct mt76_dev *dev, u8 idx, bool drop) +{ + u32 val = __mt76_rr(dev, MT_WCID_DROP(idx)); + u32 bit = MT_WCID_DROP_MASK(idx); + + /* prevent unnecessary writes */ + if ((val & bit) != (bit * drop)) + __mt76_wr(dev, MT_WCID_DROP(idx), (val & ~bit) | (bit * drop)); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_wcid_set_drop); + +void mt76x02_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq) +{ + struct mt76_txq *mtxq; + + if (!txq) + return; + + mtxq = (struct mt76_txq *) txq->drv_priv; + if (txq->sta) { + struct mt76x02_sta *sta; + + sta = (struct mt76x02_sta *) txq->sta->drv_priv; + mtxq->wcid = &sta->wcid; + } else { + struct mt76x02_vif *mvif; + + mvif = (struct mt76x02_vif *) txq->vif->drv_priv; + mtxq->wcid = &mvif->group_wcid; + } + + mt76_txq_init(dev, txq); +} +EXPORT_SYMBOL_GPL(mt76x02_txq_init); + +void mt76x02_mac_fill_txwi(struct mt76x02_txwi *txwi, struct sk_buff *skb, + struct ieee80211_sta *sta, int len, u8 nss) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 txwi_flags = 0; + + if (info->flags & IEEE80211_TX_CTL_LDPC) + txwi->rate |= cpu_to_le16(MT_RXWI_RATE_LDPC); + if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1) + txwi->rate |= cpu_to_le16(MT_RXWI_RATE_STBC); + if (nss > 1 && sta && sta->smps_mode == IEEE80211_SMPS_DYNAMIC) + txwi_flags |= MT_TXWI_FLAGS_MMPS; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ; + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + txwi->pktid |= MT_TXWI_PKTID_PROBE; + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) { + u8 ba_size = IEEE80211_MIN_AMPDU_BUF; + + ba_size <<= sta->ht_cap.ampdu_factor; + ba_size = min_t(int, 63, ba_size - 1); + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + ba_size = 0; + txwi->ack_ctl |= FIELD_PREP(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size); + + txwi_flags |= MT_TXWI_FLAGS_AMPDU | + FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, + sta->ht_cap.ampdu_density); + } + + if (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control)) + txwi_flags |= MT_TXWI_FLAGS_TS; + + txwi->flags |= cpu_to_le16(txwi_flags); + txwi->len_ctl = cpu_to_le16(len); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_fill_txwi); + +__le16 +mt76x02_mac_tx_rate_val(struct mt76_dev *dev, + const struct ieee80211_tx_rate *rate, u8 *nss_val) +{ + u16 rateval; + u8 phy, rate_idx; + u8 nss = 1; + u8 bw = 0; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 4); + phy = MT_PHY_TYPE_VHT; + if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = 2; + else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else { + const struct ieee80211_rate *r; + int band = dev->chandef.chan->band; + u16 val; + + r = &dev->hw->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + bw = 0; + } + + rateval = FIELD_PREP(MT_RXWI_RATE_INDEX, rate_idx); + rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); + rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + rateval |= MT_RXWI_RATE_SGI; + + *nss_val = nss; + return cpu_to_le16(rateval); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_tx_rate_val); + +void mt76x02_mac_wcid_set_rate(struct mt76_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate) +{ + spin_lock_bh(&dev->lock); + wcid->tx_rate = mt76x02_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss); + wcid->tx_rate_set = true; + spin_unlock_bh(&dev->lock); +} + +bool mt76x02_mac_load_tx_status(struct mt76_dev *dev, + struct mt76x02_tx_status *stat) +{ + u32 stat1, stat2; + + stat2 = __mt76_rr(dev, MT_TX_STAT_FIFO_EXT); + stat1 = __mt76_rr(dev, MT_TX_STAT_FIFO); + + stat->valid = !!(stat1 & MT_TX_STAT_FIFO_VALID); + if (!stat->valid) + return false; + + stat->success = !!(stat1 & MT_TX_STAT_FIFO_SUCCESS); + stat->aggr = !!(stat1 & MT_TX_STAT_FIFO_AGGR); + stat->ack_req = !!(stat1 & MT_TX_STAT_FIFO_ACKREQ); + stat->wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, stat1); + stat->rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, stat1); + + stat->retry = FIELD_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2); + stat->pktid = FIELD_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2); + + return true; +} +EXPORT_SYMBOL_GPL(mt76x02_mac_load_tx_status); + +static int +mt76x02_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate, + enum nl80211_band band) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); + + txrate->idx = 0; + txrate->flags = 0; + txrate->count = 1; + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (band == NL80211_BAND_2GHZ) + idx += 4; + + txrate->idx = idx; + return 0; + case MT_PHY_TYPE_CCK: + if (idx >= 8) + idx -= 8; + + txrate->idx = idx; + return 0; + case MT_PHY_TYPE_HT_GF: + txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; + /* fall through */ + case MT_PHY_TYPE_HT: + txrate->flags |= IEEE80211_TX_RC_MCS; + txrate->idx = idx; + break; + case MT_PHY_TYPE_VHT: + txrate->flags |= IEEE80211_TX_RC_VHT_MCS; + txrate->idx = idx; + break; + default: + return -EINVAL; + } + + switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case MT_PHY_BW_80: + txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + default: + return -EINVAL; + } + + if (rate & MT_RXWI_RATE_SGI) + txrate->flags |= IEEE80211_TX_RC_SHORT_GI; + + return 0; +} + +static void +mt76x02_mac_fill_tx_status(struct mt76_dev *dev, + struct ieee80211_tx_info *info, + struct mt76x02_tx_status *st, int n_frames) +{ + struct ieee80211_tx_rate *rate = info->status.rates; + int cur_idx, last_rate; + int i; + + if (!n_frames) + return; + + last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); + mt76x02_mac_process_tx_rate(&rate[last_rate], st->rate, + dev->chandef.chan->band); + if (last_rate < IEEE80211_TX_MAX_RATES - 1) + rate[last_rate + 1].idx = -1; + + cur_idx = rate[last_rate].idx + last_rate; + for (i = 0; i <= last_rate; i++) { + rate[i].flags = rate[last_rate].flags; + rate[i].idx = max_t(int, 0, cur_idx - i); + rate[i].count = 1; + } + rate[last_rate].count = st->retry + 1 - last_rate; + + info->status.ampdu_len = n_frames; + info->status.ampdu_ack_len = st->success ? n_frames : 0; + + if (st->pktid & MT_TXWI_PKTID_PROBE) + info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; + + if (st->aggr) + info->flags |= IEEE80211_TX_CTL_AMPDU | + IEEE80211_TX_STAT_AMPDU; + + if (!st->ack_req) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + else if (st->success) + info->flags |= IEEE80211_TX_STAT_ACK; +} + +void mt76x02_send_tx_status(struct mt76_dev *dev, + struct mt76x02_tx_status *stat, u8 *update) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_sta *sta = NULL; + struct mt76_wcid *wcid = NULL; + struct mt76x02_sta *msta = NULL; + + rcu_read_lock(); + if (stat->wcid < ARRAY_SIZE(dev->wcid)) + wcid = rcu_dereference(dev->wcid[stat->wcid]); + + if (wcid) { + void *priv; + + priv = msta = container_of(wcid, struct mt76x02_sta, wcid); + sta = container_of(priv, struct ieee80211_sta, + drv_priv); + } + + if (msta && stat->aggr) { + u32 stat_val, stat_cache; + + stat_val = stat->rate; + stat_val |= ((u32) stat->retry) << 16; + stat_cache = msta->status.rate; + stat_cache |= ((u32) msta->status.retry) << 16; + + if (*update == 0 && stat_val == stat_cache && + stat->wcid == msta->status.wcid && msta->n_frames < 32) { + msta->n_frames++; + goto out; + } + + mt76x02_mac_fill_tx_status(dev, &info, &msta->status, + msta->n_frames); + + msta->status = *stat; + msta->n_frames = 1; + *update = 0; + } else { + mt76x02_mac_fill_tx_status(dev, &info, stat, 1); + *update = 1; + } + + ieee80211_tx_status_noskb(dev->hw, sta, &info); + +out: + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(mt76x02_send_tx_status); + +int +mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (idx >= 8) + idx = 0; + + if (status->band == NL80211_BAND_2GHZ) + idx += 4; + + status->rate_idx = idx; + return 0; + case MT_PHY_TYPE_CCK: + if (idx >= 8) { + idx -= 8; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; + } + + if (idx >= 4) + idx = 0; + + status->rate_idx = idx; + return 0; + case MT_PHY_TYPE_HT_GF: + status->enc_flags |= RX_ENC_FLAG_HT_GF; + /* fall through */ + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + status->rate_idx = idx; + break; + case MT_PHY_TYPE_VHT: + status->encoding = RX_ENC_VHT; + status->rate_idx = FIELD_GET(MT_RATE_INDEX_VHT_IDX, idx); + status->nss = FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1; + break; + default: + return -EINVAL; + } + + if (rate & MT_RXWI_RATE_LDPC) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + if (rate & MT_RXWI_RATE_SGI) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + if (rate & MT_RXWI_RATE_STBC) + status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; + + switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + status->bw = RATE_INFO_BW_40; + break; + case MT_PHY_BW_80: + status->bw = RATE_INFO_BW_80; + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mac_process_rate); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h new file mode 100644 index 000000000000..1a5da35702e6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> + * + * 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 __MT76X02_MAC_H +#define __MT76X02_MAC_H + +#include <linux/average.h> + +struct mt76x02_tx_status { + u8 valid:1; + u8 success:1; + u8 aggr:1; + u8 ack_req:1; + u8 wcid; + u8 pktid; + u8 retry; + u16 rate; +} __packed __aligned(2); + +#define MT_VIF_WCID(_n) (254 - ((_n) & 7)) +#define MT_MAX_VIFS 8 + +struct mt76x02_vif { + u8 idx; + + struct mt76_wcid group_wcid; +}; + +DECLARE_EWMA(signal, 10, 8); + +struct mt76x02_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt76x02_vif *vif; + struct mt76x02_tx_status status; + int n_frames; + + struct ewma_signal rssi; + int inactive_count; +}; + +#define MT_RXINFO_BA BIT(0) +#define MT_RXINFO_DATA BIT(1) +#define MT_RXINFO_NULL BIT(2) +#define MT_RXINFO_FRAG BIT(3) +#define MT_RXINFO_UNICAST BIT(4) +#define MT_RXINFO_MULTICAST BIT(5) +#define MT_RXINFO_BROADCAST BIT(6) +#define MT_RXINFO_MYBSS BIT(7) +#define MT_RXINFO_CRCERR BIT(8) +#define MT_RXINFO_ICVERR BIT(9) +#define MT_RXINFO_MICERR BIT(10) +#define MT_RXINFO_AMSDU BIT(11) +#define MT_RXINFO_HTC BIT(12) +#define MT_RXINFO_RSSI BIT(13) +#define MT_RXINFO_L2PAD BIT(14) +#define MT_RXINFO_AMPDU BIT(15) +#define MT_RXINFO_DECRYPT BIT(16) +#define MT_RXINFO_BSSIDX3 BIT(17) +#define MT_RXINFO_WAPI_KEY BIT(18) +#define MT_RXINFO_PN_LEN GENMASK(21, 19) +#define MT_RXINFO_SW_FTYPE0 BIT(22) +#define MT_RXINFO_SW_FTYPE1 BIT(23) +#define MT_RXINFO_PROBE_RESP BIT(24) +#define MT_RXINFO_BEACON BIT(25) +#define MT_RXINFO_DISASSOC BIT(26) +#define MT_RXINFO_DEAUTH BIT(27) +#define MT_RXINFO_ACTION BIT(28) +#define MT_RXINFO_TCP_SUM_ERR BIT(30) +#define MT_RXINFO_IP_SUM_ERR BIT(31) + +#define MT_RXWI_CTL_WCID GENMASK(7, 0) +#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) +#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) +#define MT_RXWI_CTL_UDF GENMASK(15, 13) +#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16) +#define MT_RXWI_CTL_EOF BIT(31) + +#define MT_RXWI_TID GENMASK(3, 0) +#define MT_RXWI_SN GENMASK(15, 4) + +#define MT_RXWI_RATE_INDEX GENMASK(5, 0) +#define MT_RXWI_RATE_LDPC BIT(6) +#define MT_RXWI_RATE_BW GENMASK(8, 7) +#define MT_RXWI_RATE_SGI BIT(9) +#define MT_RXWI_RATE_STBC BIT(10) +#define MT_RXWI_RATE_LDPC_EXSYM BIT(11) +#define MT_RXWI_RATE_PHY GENMASK(15, 13) + +#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0) +#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4) + +struct mt76x02_rxwi { + __le32 rxinfo; + + __le32 ctl; + + __le16 tid_sn; + __le16 rate; + + u8 rssi[4]; + + __le32 bbp_rxinfo[4]; +}; + +#define MT_TX_PWR_ADJ GENMASK(3, 0) + +enum mt76x2_phy_bandwidth { + MT_PHY_BW_20, + MT_PHY_BW_40, + MT_PHY_BW_80, +}; + +#define MT_TXWI_FLAGS_FRAG BIT(0) +#define MT_TXWI_FLAGS_MMPS BIT(1) +#define MT_TXWI_FLAGS_CFACK BIT(2) +#define MT_TXWI_FLAGS_TS BIT(3) +#define MT_TXWI_FLAGS_AMPDU BIT(4) +#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) +#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) +#define MT_TXWI_FLAGS_NDPS BIT(10) +#define MT_TXWI_FLAGS_RTSBWSIG BIT(11) +#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12) +#define MT_TXWI_FLAGS_SOUND BIT(14) +#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) + +#define MT_TXWI_ACK_CTL_REQ BIT(0) +#define MT_TXWI_ACK_CTL_NSEQ BIT(1) +#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) + +#define MT_TXWI_PKTID_PROBE BIT(7) + +struct mt76x02_txwi { + __le16 flags; + __le16 rate; + u8 ack_ctl; + u8 wcid; + __le16 len_ctl; + __le32 iv; + __le32 eiv; + u8 aid; + u8 txstream; + u8 ctl2; + u8 pktid; +} __packed __aligned(4); + +static inline bool mt76x02_wait_for_mac(struct mt76_dev *dev) +{ + const u32 MAC_CSR0 = 0x1000; + int i; + + for (i = 0; i < 500; i++) { + if (test_bit(MT76_REMOVED, &dev->state)) + return -EIO; + + switch (dev->bus->rr(dev, MAC_CSR0)) { + case 0: + case ~0: + break; + default: + return true; + } + usleep_range(5000, 10000); + } + return false; +} + +void mt76x02_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq); +void mt76x02_mac_fill_txwi(struct mt76x02_txwi *txwi, struct sk_buff *skb, + struct ieee80211_sta *sta, int len, u8 nss); +enum mt76x02_cipher_type +mt76x02_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data); + +int mt76x02_mac_shared_key_setup(struct mt76_dev *dev, u8 vif_idx, u8 key_idx, + struct ieee80211_key_conf *key); +int mt76x02_mac_wcid_set_key(struct mt76_dev *dev, u8 idx, + struct ieee80211_key_conf *key); +void mt76x02_mac_wcid_setup(struct mt76_dev *dev, u8 idx, u8 vif_idx, u8 *mac); +void mt76x02_mac_wcid_set_drop(struct mt76_dev *dev, u8 idx, bool drop); +void mt76x02_mac_wcid_set_rate(struct mt76_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate); +__le16 +mt76x02_mac_tx_rate_val(struct mt76_dev *dev, + const struct ieee80211_tx_rate *rate, u8 *nss_val); +bool mt76x02_mac_load_tx_status(struct mt76_dev *dev, + struct mt76x02_tx_status *stat); +void mt76x02_send_tx_status(struct mt76_dev *dev, + struct mt76x02_tx_status *stat, u8 *update); +int +mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c new file mode 100644 index 000000000000..5a2fba3462fd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * 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/kernel.h> +#include <linux/firmware.h> +#include <linux/delay.h> + +#include "mt76.h" +#include "mt76x02_mcu.h" +#include "mt76x02_dma.h" + +struct sk_buff *mt76x02_mcu_msg_alloc(const void *data, int len) +{ + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + memcpy(skb_put(skb, len), data, len); + + return skb; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_alloc); + +static struct sk_buff * +mt76x02_mcu_get_response(struct mt76_dev *dev, unsigned long expires) +{ + unsigned long timeout; + + if (!time_is_after_jiffies(expires)) + return NULL; + + timeout = expires - jiffies; + wait_event_timeout(dev->mmio.mcu.wait, + !skb_queue_empty(&dev->mmio.mcu.res_q), + timeout); + return skb_dequeue(&dev->mmio.mcu.res_q); +} + +static int +mt76x02_tx_queue_mcu(struct mt76_dev *dev, enum mt76_txq_id qid, + struct sk_buff *skb, int cmd, int seq) +{ + struct mt76_queue *q = &dev->q_tx[qid]; + struct mt76_queue_buf buf; + dma_addr_t addr; + u32 tx_info; + + tx_info = MT_MCU_MSG_TYPE_CMD | + FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | + FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | + FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | + FIELD_PREP(MT_MCU_MSG_LEN, skb->len); + + addr = dma_map_single(dev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev->dev, addr)) + return -ENOMEM; + + buf.addr = addr; + buf.len = skb->len; + spin_lock_bh(&q->lock); + dev->queue_ops->add_buf(dev, q, &buf, 1, tx_info, skb, NULL); + dev->queue_ops->kick(dev, q); + spin_unlock_bh(&q->lock); + + return 0; +} + +int mt76x02_mcu_msg_send(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp) +{ + unsigned long expires = jiffies + HZ; + int ret; + u8 seq; + + if (!skb) + return -EINVAL; + + mutex_lock(&dev->mmio.mcu.mutex); + + seq = ++dev->mmio.mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mmio.mcu.msg_seq & 0xf; + + ret = mt76x02_tx_queue_mcu(dev, MT_TXQ_MCU, skb, cmd, seq); + if (ret) + goto out; + + while (wait_resp) { + u32 *rxfce; + bool check_seq = false; + + skb = mt76x02_mcu_get_response(dev, expires); + if (!skb) { + dev_err(dev->dev, + "MCU message %d (seq %d) timed out\n", cmd, + seq); + ret = -ETIMEDOUT; + break; + } + + rxfce = (u32 *) skb->cb; + + if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) + check_seq = true; + + dev_kfree_skb(skb); + if (check_seq) + break; + } + +out: + mutex_unlock(&dev->mmio.mcu.mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send); + +int mt76x02_mcu_function_select(struct mt76_dev *dev, + enum mcu_function func, + u32 val, bool wait_resp) +{ + struct sk_buff *skb; + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(func), + .value = cpu_to_le32(val), + }; + + skb = dev->mcu_ops->mcu_msg_alloc(&msg, sizeof(msg)); + return dev->mcu_ops->mcu_send_msg(dev, skb, CMD_FUN_SET_OP, + wait_resp); +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_function_select); + +int mt76x02_mcu_set_radio_state(struct mt76_dev *dev, bool on, + bool wait_resp) +{ + struct sk_buff *skb; + struct { + __le32 mode; + __le32 level; + } __packed __aligned(4) msg = { + .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), + .level = cpu_to_le32(0), + }; + + skb = dev->mcu_ops->mcu_msg_alloc(&msg, sizeof(msg)); + return dev->mcu_ops->mcu_send_msg(dev, skb, CMD_POWER_SAVING_OP, + wait_resp); +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state); + +int mt76x02_mcu_calibrate(struct mt76_dev *dev, int type, + u32 param, bool wait) +{ + struct sk_buff *skb; + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(type), + .value = cpu_to_le32(param), + }; + int ret; + + if (wait) + dev->bus->rmw(dev, MT_MCU_COM_REG0, BIT(31), 0); + + skb = dev->mcu_ops->mcu_msg_alloc(&msg, sizeof(msg)); + ret = dev->mcu_ops->mcu_send_msg(dev, skb, CMD_CALIBRATION_OP, true); + if (ret) + return ret; + + if (wait && + WARN_ON(!__mt76_poll_msec(dev, MT_MCU_COM_REG0, + BIT(31), BIT(31), 100))) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate); + +int mt76x02_mcu_cleanup(struct mt76_dev *dev) +{ + struct sk_buff *skb; + + dev->bus->wr(dev, MT_MCU_INT_LEVEL, 1); + usleep_range(20000, 30000); + + while ((skb = skb_dequeue(&dev->mmio.mcu.res_q)) != NULL) + dev_kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h new file mode 100644 index 000000000000..2f5af3dad2bb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * 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 __MT76x02_MCU_H +#define __MT76x0x_MCU_H + +#define MT_MCU_RESET_CTL 0x070C +#define MT_MCU_INT_LEVEL 0x0718 +#define MT_MCU_COM_REG0 0x0730 +#define MT_MCU_COM_REG1 0x0734 +#define MT_MCU_COM_REG2 0x0738 +#define MT_MCU_COM_REG3 0x073C + +#define MT_INBAND_PACKET_MAX_LEN 192 +#define MT_MCU_MEMMAP_WLAN 0x410000 + +enum mcu_cmd { + CMD_FUN_SET_OP = 1, + CMD_LOAD_CR = 2, + CMD_INIT_GAIN_OP = 3, + CMD_DYNC_VGA_OP = 6, + CMD_TDLS_CH_SW = 7, + CMD_BURST_WRITE = 8, + CMD_READ_MODIFY_WRITE = 9, + CMD_RANDOM_READ = 10, + CMD_BURST_READ = 11, + CMD_RANDOM_WRITE = 12, + CMD_LED_MODE_OP = 16, + CMD_POWER_SAVING_OP = 20, + CMD_WOW_CONFIG = 21, + CMD_WOW_QUERY = 22, + CMD_WOW_FEATURE = 24, + CMD_CARRIER_DETECT_OP = 28, + CMD_RADOR_DETECT_OP = 29, + CMD_SWITCH_CHANNEL_OP = 30, + CMD_CALIBRATION_OP = 31, + CMD_BEACON_OP = 32, + CMD_ANTENNA_OP = 33, +}; + +enum mcu_power_mode { + RADIO_OFF = 0x30, + RADIO_ON = 0x31, + RADIO_OFF_AUTO_WAKEUP = 0x32, + RADIO_OFF_ADVANCE = 0x33, + RADIO_ON_ADVANCE = 0x34, +}; + +enum mcu_function { + Q_SELECT = 1, + BW_SETTING = 2, + USB2_SW_DISCONNECT = 2, + USB3_SW_DISCONNECT = 3, + LOG_FW_DEBUG_MSG = 4, + GET_FW_VERSION = 5, +}; + +struct mt76x02_fw_header { + __le32 ilm_len; + __le32 dlm_len; + __le16 build_ver; + __le16 fw_ver; + u8 pad[4]; + char build_time[16]; +}; + +struct mt76x02_patch_header { + char build_time[16]; + char platform[4]; + char hw_version[4]; + char patch_version[4]; + u8 pad[2]; +}; + +int mt76x02_mcu_cleanup(struct mt76_dev *dev); +int mt76x02_mcu_calibrate(struct mt76_dev *dev, int type, + u32 param, bool wait); +struct sk_buff *mt76x02_mcu_msg_alloc(const void *data, int len); +int mt76x02_mcu_msg_send(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp); +int mt76x02_mcu_function_select(struct mt76_dev *dev, + enum mcu_function func, + u32 val, bool wait_resp); +int mt76x02_mcu_set_radio_state(struct mt76_dev *dev, bool on, + bool wait_resp); + +#endif /* __MT76x02_MCU_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h index 1551ea453180..24d1e6d747dd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h @@ -14,8 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef __MT76x2_REGS_H -#define __MT76x2_REGS_H +#ifndef __MT76X02_REGS_H +#define __MT76X02_REGS_H #define MT_ASIC_VERSION 0x0000 @@ -46,6 +46,11 @@ #define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1) #define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2) +#define MT_COEXCFG3 0x004c + +#define MT_LDO_CTRL_0 0x006c +#define MT_LDO_CTRL_1 0x0070 + #define MT_WLAN_FUN_CTRL_WLAN_RESET BIT(3) /* MT76x0 */ #define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) /* MT76x2 */ @@ -75,6 +80,8 @@ #define MT_XO_CTRL7 0x011c +#define MT_IOCFG_6 0x0124 + #define MT_USB_U3DMA_CFG 0x9018 #define MT_USB_DMA_CFG_RX_BULK_AGG_TOUT GENMASK(7, 0) #define MT_USB_DMA_CFG_RX_BULK_AGG_LMT GENMASK(15, 8) @@ -156,18 +163,23 @@ #define MT_WMM_TXOP_SHIFT(_n) ((_n & 1) * 16) #define MT_WMM_TXOP_MASK GENMASK(15, 0) +#define MT_WMM_CTRL 0x0230 /* MT76x0 */ +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 +#define MT_USB_DMA_CFG 0x0238 + #define MT_TSO_CTRL 0x0250 #define MT_HEADER_TRANS_CTRL_REG 0x0260 +#define MT_US_CYC_CFG 0x02a4 +#define MT_US_CYC_CNT GENMASK(7, 0) + #define MT_TX_RING_BASE 0x0300 #define MT_RX_RING_BASE 0x03c0 #define MT_TX_HW_QUEUE_MCU 8 #define MT_TX_HW_QUEUE_MGMT 9 -#define MT_US_CYC_CFG 0x02a4 -#define MT_US_CYC_CNT GENMASK(7, 0) - #define MT_PBF_SYS_CTRL 0x0400 #define MT_PBF_SYS_CTRL_MCU_RESET BIT(0) #define MT_PBF_SYS_CTRL_DMA_RESET BIT(1) @@ -189,10 +201,20 @@ #define MT_BCN_OFFSET_BASE 0x041c #define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2)) +#define MT_RXQ_STA 0x0430 +#define MT_TXQ_STA 0x0434 +#define MT_RF_CSR_CFG 0x0500 +#define MT_RF_CSR_CFG_DATA GENMASK(7, 0) +#define MT_RF_CSR_CFG_REG_ID GENMASK(13, 8) +#define MT_RF_CSR_CFG_REG_BANK GENMASK(17, 14) +#define MT_RF_CSR_CFG_WR BIT(30) +#define MT_RF_CSR_CFG_KICK BIT(31) + #define MT_RF_BYPASS_0 0x0504 #define MT_RF_BYPASS_1 0x0508 #define MT_RF_SETTING_0 0x050c +#define MT_RF_MISC 0x0518 #define MT_RF_DATA_WRITE 0x0524 #define MT_RF_CTRL 0x0528 @@ -203,6 +225,11 @@ #define MT_RF_DATA_READ 0x052c +#define MT_COM_REG0 0x0730 +#define MT_COM_REG1 0x0734 +#define MT_COM_REG2 0x0738 +#define MT_COM_REG3 0x073C + #define MT_FCE_PSE_CTRL 0x0800 #define MT_FCE_PARAMETERS 0x0804 #define MT_FCE_CSO 0x0808 @@ -222,6 +249,7 @@ #define MT_TX_CPU_FROM_FCE_BASE_PTR 0x09a0 #define MT_TX_CPU_FROM_FCE_MAX_COUNT 0x09a4 +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 #define MT_FCE_PDMA_GLOBAL_CONF 0x09c4 #define MT_FCE_SKIP_FS 0x0a6c @@ -250,6 +278,9 @@ #define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24) #define MT_MAX_LEN_CFG 0x1018 +#define MT_MAX_LEN_CFG_AMPDU GENMASK(13, 12) + +#define MT_LED_CFG 0x102c #define MT_AMPDU_MAX_LEN_20M1S 0x1030 #define MT_AMPDU_MAX_LEN_20M2S 0x1034 @@ -365,6 +396,8 @@ #define MT_TX_SW_CFG2 0x1338 #define MT_TXOP_CTRL_CFG 0x1340 +#define MT_TXOP_TRUN_EN GENMASK(5, 0) +#define MT_TXOP_EXT_CCA_DLY GENMASK(15, 8) #define MT_TX_RTS_CFG 0x1344 #define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0) @@ -376,7 +409,10 @@ #define MT_TX_RETRY_CFG 0x134c #define MT_TX_LINK_CFG 0x1350 +#define MT_VHT_HT_FBK_CFG0 0x1354 #define MT_VHT_HT_FBK_CFG1 0x1358 +#define MT_LG_FBK_CFG0 0x135c +#define MT_LG_FBK_CFG1 0x1360 #define MT_PROT_CFG_RATE GENMASK(15, 0) #define MT_PROT_CFG_CTRL GENMASK(17, 16) @@ -391,6 +427,27 @@ #define MT_GF20_PROT_CFG 0x1374 #define MT_GF40_PROT_CFG 0x1378 +#define MT_PROT_RATE GENMASK(15, 0) +#define MT_PROT_CTRL_RTS_CTS BIT(16) +#define MT_PROT_CTRL_CTS2SELF BIT(17) +#define MT_PROT_NAV_SHORT BIT(18) +#define MT_PROT_NAV_LONG BIT(19) +#define MT_PROT_TXOP_ALLOW_CCK BIT(20) +#define MT_PROT_TXOP_ALLOW_OFDM BIT(21) +#define MT_PROT_TXOP_ALLOW_MM20 BIT(22) +#define MT_PROT_TXOP_ALLOW_MM40 BIT(23) +#define MT_PROT_TXOP_ALLOW_GF20 BIT(24) +#define MT_PROT_TXOP_ALLOW_GF40 BIT(25) +#define MT_PROT_RTS_THR_EN BIT(26) +#define MT_PROT_RATE_CCK_11 0x0003 +#define MT_PROT_RATE_OFDM_6 0x4000 +#define MT_PROT_RATE_OFDM_24 0x4004 +#define MT_PROT_RATE_DUP_OFDM_24 0x4084 +#define MT_PROT_TXOP_ALLOW_ALL GENMASK(25, 20) +#define MT_PROT_TXOP_ALLOW_BW20 (MT_PROT_TXOP_ALLOW_ALL & \ + ~MT_PROT_TXOP_ALLOW_MM40 & \ + ~MT_PROT_TXOP_ALLOW_GF40) + #define MT_EXP_ACK_TIME 0x1380 #define MT_TX_PWR_CFG_0_EXT 0x1390 @@ -405,6 +462,8 @@ #define MT_TX0_RF_GAIN_CORR 0x13a0 #define MT_TX1_RF_GAIN_CORR 0x13a4 +#define MT_TX0_RF_GAIN_ATTEN 0x13a8 +#define MT_TX0_RF_GAIN_ATTEN 0x13a8 /* MT76x0 */ #define MT_TX_ALC_CFG_0 0x13b0 #define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0) @@ -421,6 +480,7 @@ #define MT_TX_ALC_CFG_3 0x13ac #define MT_TX_ALC_CFG_4 0x13c0 #define MT_TX_ALC_CFG_4_LOWGAIN_CH_EN BIT(31) +#define MT_TX0_BB_GAIN_ATTEN 0x13c0 /* MT76x0 */ #define MT_TX_ALC_VGA3 0x13c8 @@ -451,10 +511,13 @@ #define MT_RX_FILTR_CFG_CTRL_RSV BIT(16) #define MT_AUTO_RSP_CFG 0x1404 +#define MT_AUTO_RSP_PREAMB_SHORT BIT(4) #define MT_LEGACY_BASIC_RATE 0x1408 #define MT_HT_BASIC_RATE 0x140c #define MT_HT_CTRL_CFG 0x1410 +#define MT_RX_PARSER_CFG 0x1418 +#define MT_RX_PARSER_RX_SET_NAV_ALL BIT(0) #define MT_EXT_CCA_CFG 0x141c #define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0) @@ -498,7 +561,10 @@ #define MT_TX_STAT_FIFO_WCID GENMASK(15, 8) #define MT_TX_STAT_FIFO_RATE GENMASK(31, 16) +#define MT_TX_AGG_STAT 0x171c + #define MT_TX_AGG_CNT_BASE0 0x1720 +#define MT_MPDU_DENSITY_CNT 0x1740 #define MT_TX_AGG_CNT_BASE1 0x174c #define MT_TX_AGG_CNT(_id) ((_id) < 8 ? \ @@ -604,7 +670,7 @@ struct mt76_wcid_key { u8 rx_mic[8]; } __packed __aligned(4); -enum mt76x2_cipher_type { +enum mt76x02_cipher_type { MT_CIPHER_NONE, MT_CIPHER_WEP40, MT_CIPHER_WEP104, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_dma.h b/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h index da294558c268..2482f9761fcd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_dma.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,16 +14,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef __MT76x2_DMA_H -#define __MT76x2_DMA_H +#ifndef __MT76x02_USB_H +#define __MT76x0x_USB_H -#include "dma.h" +#include "mt76.h" -enum mt76x2_qsel { - MT_QSEL_MGMT, - MT_QSEL_HCCA, - MT_QSEL_EDCA, - MT_QSEL_EDCA_2, -}; +void mt76x02u_init_mcu(struct mt76_dev *dev); +void mt76x02u_mcu_fw_reset(struct mt76_dev *dev); +int mt76x02u_mcu_fw_send_data(struct mt76_dev *dev, const void *data, + int data_len, u32 max_payload, u32 offset); -#endif +int mt76x02u_skb_dma_info(struct sk_buff *skb, int port, u32 flags); +int mt76x02u_set_txinfo(struct sk_buff *skb, struct mt76_wcid *wcid, u8 ep); +#endif /* __MT76x02_USB_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c new file mode 100644 index 000000000000..235b1bc5a367 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * 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" +#include "mt76x02_dma.h" + +int mt76x02u_skb_dma_info(struct sk_buff *skb, int port, u32 flags) +{ + struct sk_buff *iter, *last = skb; + u32 info, pad; + + /* Buffer layout: + * | 4B | xfer len | pad | 4B | + * | TXINFO | pkt/cmd | zero pad to 4B | zero | + * + * length field of TXINFO should be set to 'xfer len'. + */ + info = FIELD_PREP(MT_TXD_INFO_LEN, round_up(skb->len, 4)) | + FIELD_PREP(MT_TXD_INFO_DPORT, port) | flags; + put_unaligned_le32(info, skb_push(skb, sizeof(info))); + + pad = round_up(skb->len, 4) + 4 - skb->len; + skb_walk_frags(skb, iter) { + last = iter; + if (!iter->next) { + skb->data_len += pad; + skb->len += pad; + break; + } + } + + if (unlikely(pad)) { + if (__skb_pad(last, pad, true)) + return -ENOMEM; + __skb_put(last, pad); + } + return 0; +} + +int mt76x02u_set_txinfo(struct sk_buff *skb, struct mt76_wcid *wcid, u8 ep) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + enum mt76_qsel qsel; + u32 flags; + + if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) || + ep == MT_EP_OUT_HCCA) + qsel = MT_QSEL_MGMT; + else + qsel = MT_QSEL_EDCA; + + flags = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) | + MT_TXD_INFO_80211; + if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv) + flags |= MT_TXD_INFO_WIV; + + return mt76x02u_skb_dma_info(skb, WLAN_PORT, flags); +} +EXPORT_SYMBOL_GPL(mt76x02u_set_txinfo); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c new file mode 100644 index 000000000000..b39a4d7d71cc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/firmware.h> + +#include "mt76.h" +#include "mt76x02_dma.h" +#include "mt76x02_mcu.h" +#include "mt76x02_usb.h" + +#define MT_CMD_HDR_LEN 4 + +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 + +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 + +static struct sk_buff * +mt76x02u_mcu_msg_alloc(const void *data, int len) +{ + struct sk_buff *skb; + + skb = alloc_skb(MT_CMD_HDR_LEN + len + 8, GFP_KERNEL); + if (!skb) + return NULL; + + skb_reserve(skb, MT_CMD_HDR_LEN); + skb_put_data(skb, data, len); + + return skb; +} + +static void +mt76x02u_multiple_mcu_reads(struct mt76_dev *dev, u8 *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + u32 reg, val; + int i; + + if (usb->mcu.burst) { + WARN_ON_ONCE(len / 4 != usb->mcu.rp_len); + + reg = usb->mcu.rp[0].reg - usb->mcu.base; + for (i = 0; i < usb->mcu.rp_len; i++) { + val = get_unaligned_le32(data + 4 * i); + usb->mcu.rp[i].reg = reg++; + usb->mcu.rp[i].value = val; + } + } else { + WARN_ON_ONCE(len / 8 != usb->mcu.rp_len); + + for (i = 0; i < usb->mcu.rp_len; i++) { + reg = get_unaligned_le32(data + 8 * i) - + usb->mcu.base; + val = get_unaligned_le32(data + 8 * i + 4); + + WARN_ON_ONCE(usb->mcu.rp[i].reg != reg); + usb->mcu.rp[i].value = val; + } + } +} + +static int mt76x02u_mcu_wait_resp(struct mt76_dev *dev, u8 seq) +{ + struct mt76_usb *usb = &dev->usb; + struct mt76u_buf *buf = &usb->mcu.res; + struct urb *urb = buf->urb; + int i, ret; + u32 rxfce; + u8 *data; + + for (i = 0; i < 5; i++) { + if (!wait_for_completion_timeout(&usb->mcu.cmpl, + msecs_to_jiffies(300))) + continue; + + if (urb->status) + return -EIO; + + data = sg_virt(&urb->sg[0]); + if (usb->mcu.rp) + mt76x02u_multiple_mcu_reads(dev, data + 4, + urb->actual_length - 8); + + rxfce = get_unaligned_le32(data); + ret = mt76u_submit_buf(dev, USB_DIR_IN, + MT_EP_IN_CMD_RESP, + buf, GFP_KERNEL, + mt76u_mcu_complete_urb, + &usb->mcu.cmpl); + if (ret) + return ret; + + if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce) && + FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce) == EVT_CMD_DONE) + return 0; + + dev_err(dev->dev, "error: MCU resp evt:%lx seq:%hhx-%lx\n", + FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce), + seq, FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce)); + } + + dev_err(dev->dev, "error: %s timed out\n", __func__); + return -ETIMEDOUT; +} + +static int +__mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp) +{ + struct usb_interface *intf = to_usb_interface(dev->dev); + struct usb_device *udev = interface_to_usbdev(intf); + struct mt76_usb *usb = &dev->usb; + unsigned int pipe; + int ret, sent; + u8 seq = 0; + u32 info; + + if (!skb) + return -EINVAL; + + if (test_bit(MT76_REMOVED, &dev->state)) + return 0; + + pipe = usb_sndbulkpipe(udev, usb->out_ep[MT_EP_OUT_INBAND_CMD]); + if (wait_resp) { + seq = ++usb->mcu.msg_seq & 0xf; + if (!seq) + seq = ++usb->mcu.msg_seq & 0xf; + } + + info = FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | + FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | + MT_MCU_MSG_TYPE_CMD; + ret = mt76x02u_skb_dma_info(skb, CPU_TX_PORT, info); + if (ret) + return ret; + + ret = usb_bulk_msg(udev, pipe, skb->data, skb->len, &sent, 500); + if (ret) + return ret; + + if (wait_resp) + ret = mt76x02u_mcu_wait_resp(dev, seq); + + consume_skb(skb); + + return ret; +} + +static int +mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp) +{ + struct mt76_usb *usb = &dev->usb; + int err; + + mutex_lock(&usb->mcu.mutex); + err = __mt76x02u_mcu_send_msg(dev, skb, cmd, wait_resp); + mutex_unlock(&usb->mcu.mutex); + + return err; +} + +static inline void skb_put_le32(struct sk_buff *skb, u32 val) +{ + put_unaligned_le32(val, skb_put(skb, 4)); +} + +static int +mt76x02u_mcu_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int n) +{ + const int CMD_RANDOM_WRITE = 12; + const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8; + struct sk_buff *skb; + int cnt, i, ret; + + if (!n) + return 0; + + cnt = min(max_vals_per_cmd, n); + + skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_reserve(skb, MT_DMA_HDR_LEN); + + for (i = 0; i < cnt; i++) { + skb_put_le32(skb, base + data[i].reg); + skb_put_le32(skb, data[i].value); + } + + ret = mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_WRITE, cnt == n); + if (ret) + return ret; + + return mt76x02u_mcu_wr_rp(dev, base, data + cnt, n - cnt); +} + +static int +mt76x02u_mcu_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int n) +{ + const int CMD_RANDOM_READ = 10; + const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8; + struct mt76_usb *usb = &dev->usb; + struct sk_buff *skb; + int cnt, i, ret; + + if (!n) + return 0; + + cnt = min(max_vals_per_cmd, n); + if (cnt != n) + return -EINVAL; + + skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_reserve(skb, MT_DMA_HDR_LEN); + + for (i = 0; i < cnt; i++) { + skb_put_le32(skb, base + data[i].reg); + skb_put_le32(skb, data[i].value); + } + + mutex_lock(&usb->mcu.mutex); + + usb->mcu.rp = data; + usb->mcu.rp_len = n; + usb->mcu.base = base; + usb->mcu.burst = false; + + ret = __mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_READ, true); + + usb->mcu.rp = NULL; + + mutex_unlock(&usb->mcu.mutex); + + return ret; +} + +void mt76x02u_mcu_fw_reset(struct mt76_dev *dev) +{ + mt76u_vendor_request(dev, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x1, 0, NULL, 0); +} +EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_reset); + +static int +__mt76x02u_mcu_fw_send_data(struct mt76_dev *dev, struct mt76u_buf *buf, + const void *fw_data, int len, u32 dst_addr) +{ + u8 *data = sg_virt(&buf->urb->sg[0]); + DECLARE_COMPLETION_ONSTACK(cmpl); + __le32 info; + u32 val; + int err; + + info = cpu_to_le32(FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | + FIELD_PREP(MT_MCU_MSG_LEN, len) | + MT_MCU_MSG_TYPE_CMD); + + memcpy(data, &info, sizeof(info)); + memcpy(data + sizeof(info), fw_data, len); + memset(data + sizeof(info) + len, 0, 4); + + mt76u_single_wr(dev, MT_VEND_WRITE_FCE, + MT_FCE_DMA_ADDR, dst_addr); + len = roundup(len, 4); + mt76u_single_wr(dev, MT_VEND_WRITE_FCE, + MT_FCE_DMA_LEN, len << 16); + + buf->len = MT_CMD_HDR_LEN + len + sizeof(info); + err = mt76u_submit_buf(dev, USB_DIR_OUT, + MT_EP_OUT_INBAND_CMD, + buf, GFP_KERNEL, + mt76u_mcu_complete_urb, &cmpl); + if (err < 0) + return err; + + if (!wait_for_completion_timeout(&cmpl, + msecs_to_jiffies(1000))) { + dev_err(dev->dev, "firmware upload timed out\n"); + usb_kill_urb(buf->urb); + return -ETIMEDOUT; + } + + if (mt76u_urb_error(buf->urb)) { + dev_err(dev->dev, "firmware upload failed: %d\n", + buf->urb->status); + return buf->urb->status; + } + + val = mt76u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); + val++; + mt76u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); + + return 0; +} + +int mt76x02u_mcu_fw_send_data(struct mt76_dev *dev, const void *data, + int data_len, u32 max_payload, u32 offset) +{ + int err, len, pos = 0, max_len = max_payload - 8; + struct mt76u_buf buf; + + err = mt76u_buf_alloc(dev, &buf, 1, max_payload, max_payload, + GFP_KERNEL); + if (err < 0) + return err; + + while (data_len > 0) { + len = min_t(int, data_len, max_len); + err = __mt76x02u_mcu_fw_send_data(dev, &buf, data + pos, + len, offset + pos); + if (err < 0) + break; + + data_len -= len; + pos += len; + usleep_range(5000, 10000); + } + mt76u_buf_free(&buf); + + return err; +} +EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_send_data); + +void mt76x02u_init_mcu(struct mt76_dev *dev) +{ + static const struct mt76_mcu_ops mt76x02u_mcu_ops = { + .mcu_msg_alloc = mt76x02u_mcu_msg_alloc, + .mcu_send_msg = mt76x02u_mcu_send_msg, + .mcu_wr_rp = mt76x02u_mcu_wr_rp, + .mcu_rd_rp = mt76x02u_mcu_rd_rp, + }; + + dev->mcu_ops = &mt76x02u_mcu_ops; +} +EXPORT_SYMBOL_GPL(mt76x02u_init_mcu); + +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c new file mode 100644 index 000000000000..4ecfb75f3f7d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> + * 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" +#include "mt76x02_dma.h" +#include "mt76x02_regs.h" +#include "mt76x02_mac.h" + +#define CCK_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ + .hw_value = (MT_PHY_TYPE_CCK << 8) | _idx, \ + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + _idx), \ +} + +#define OFDM_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .hw_value = (MT_PHY_TYPE_OFDM << 8) | _idx, \ + .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | _idx, \ +} + +struct ieee80211_rate mt76x02_rates[] = { + CCK_RATE(0, 10), + CCK_RATE(1, 20), + CCK_RATE(2, 55), + CCK_RATE(3, 110), + OFDM_RATE(0, 60), + OFDM_RATE(1, 90), + OFDM_RATE(2, 120), + OFDM_RATE(3, 180), + OFDM_RATE(4, 240), + OFDM_RATE(5, 360), + OFDM_RATE(6, 480), + OFDM_RATE(7, 540), +}; +EXPORT_SYMBOL_GPL(mt76x02_rates); + +void mt76x02_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct mt76_dev *dev = hw->priv; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + dev->rxfilter &= ~(_hw); \ + dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mutex); + + dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; + + MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); + MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); + MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | + MT_RX_FILTR_CFG_CTS | + MT_RX_FILTR_CFG_CFEND | + MT_RX_FILTR_CFG_CFACK | + MT_RX_FILTR_CFG_BA | + MT_RX_FILTR_CFG_CTRL_RSV); + MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); + + *total_flags = flags; + dev->bus->wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76x02_configure_filter); + +int mt76x02_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_dev *dev = hw->priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; + int ret = 0; + int idx = 0; + int i; + + mutex_lock(&dev->mutex); + + idx = mt76_wcid_alloc(dev->wcid_mask, ARRAY_SIZE(dev->wcid)); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + + msta->vif = mvif; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + msta->wcid.hw_key_idx = -1; + mt76x02_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); + mt76x02_mac_wcid_set_drop(dev, idx, false); + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + mt76x02_txq_init(dev, sta->txq[i]); + + if (vif->type == NL80211_IFTYPE_AP) + set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags); + + ewma_signal_init(&msta->rssi); + + rcu_assign_pointer(dev->wcid[idx], &msta->wcid); + +out: + mutex_unlock(&dev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76x02_sta_add); + +int mt76x02_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_dev *dev = hw->priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv; + int idx = msta->wcid.idx; + int i; + + mutex_lock(&dev->mutex); + rcu_assign_pointer(dev->wcid[idx], NULL); + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + mt76_txq_remove(dev, sta->txq[i]); + mt76x02_mac_wcid_set_drop(dev, idx, true); + mt76_wcid_free(dev->wcid_mask, idx); + mt76x02_mac_wcid_setup(dev, idx, 0, NULL); + mutex_unlock(&dev->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_sta_remove); + +void mt76x02_vif_init(struct mt76_dev *dev, struct ieee80211_vif *vif, + unsigned int idx) +{ + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; + + mvif->idx = idx; + mvif->group_wcid.idx = MT_VIF_WCID(idx); + mvif->group_wcid.hw_key_idx = -1; + mt76x02_txq_init(dev, vif->txq); +} +EXPORT_SYMBOL_GPL(mt76x02_vif_init); + +int +mt76x02_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76_dev *dev = hw->priv; + unsigned int idx = 0; + + if (vif->addr[0] & BIT(1)) + idx = 1 + (((dev->macaddr[0] ^ vif->addr[0]) >> 2) & 7); + + /* + * Client mode typically only has one configurable BSSID register, + * which is used for bssidx=0. This is linked to the MAC address. + * Since mac80211 allows changing interface types, and we cannot + * force the use of the primary MAC address for a station mode + * interface, we need some other way of configuring a per-interface + * remote BSSID. + * The hardware provides an AP-Client feature, where bssidx 0-7 are + * used for AP mode and bssidx 8-15 for client mode. + * We shift the station interface bss index by 8 to force the + * hardware to recognize the BSSID. + * The resulting bssidx mismatch for unicast frames is ignored by hw. + */ + if (vif->type == NL80211_IFTYPE_STATION) + idx += 8; + + mt76x02_vif_init(dev, vif, idx); + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_add_interface); + +void mt76x02_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt76_dev *dev = hw->priv; + + mt76_txq_remove(dev, vif->txq); +} +EXPORT_SYMBOL_GPL(mt76x02_remove_interface); + +int mt76x02_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct ieee80211_sta *sta = params->sta; + struct mt76_dev *dev = hw->priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv; + struct ieee80211_txq *txq = sta->txq[params->tid]; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + struct mt76_txq *mtxq; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(dev, &msta->wcid, tid, *ssn, params->buf_size); + __mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(dev, &msta->wcid, tid); + __mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); + break; + case IEEE80211_AMPDU_TX_START: + mtxq->agg_ssn = *ssn << 4; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_ampdu_action); + +int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt76_dev *dev = hw->priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; + struct mt76x02_sta *msta; + struct mt76_wcid *wcid; + int idx = key->keyidx; + int ret; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + break; + default: + return -EOPNOTSUPP; + } + + /* + * The hardware does not support per-STA RX GTK, fall back + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + msta = sta ? (struct mt76x02_sta *) sta->drv_priv : NULL; + wcid = msta ? &msta->wcid : &mvif->group_wcid; + + if (cmd == SET_KEY) { + key->hw_key_idx = wcid->idx; + wcid->hw_key_idx = idx; + if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + wcid->sw_iv = true; + } + } else { + if (idx == wcid->hw_key_idx) { + wcid->hw_key_idx = -1; + wcid->sw_iv = true; + } + + key = NULL; + } + mt76_wcid_key_setup(dev, wcid, key); + + if (!msta) { + if (key || wcid->hw_key_idx == idx) { + ret = mt76x02_mac_wcid_set_key(dev, wcid->idx, key); + if (ret) + return ret; + } + + return mt76x02_mac_shared_key_setup(dev, mvif->idx, idx, key); + } + + return mt76x02_mac_wcid_set_key(dev, msta->wcid.idx, key); +} +EXPORT_SYMBOL_GPL(mt76x02_set_key); + +int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct mt76_dev *dev = hw->priv; + u8 cw_min = 5, cw_max = 10, qid; + u32 val; + + qid = dev->q_tx[queue].hw_idx; + + if (params->cw_min) + cw_min = fls(params->cw_min); + if (params->cw_max) + cw_max = fls(params->cw_max); + + val = FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop) | + FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) | + FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) | + FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max); + __mt76_wr(dev, MT_EDCA_CFG_AC(qid), val); + + val = __mt76_rr(dev, MT_WMM_TXOP(qid)); + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(qid)); + val |= params->txop << MT_WMM_TXOP_SHIFT(qid); + __mt76_wr(dev, MT_WMM_TXOP(qid), val); + + val = __mt76_rr(dev, MT_WMM_AIFSN); + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(qid)); + val |= params->aifs << MT_WMM_AIFSN_SHIFT(qid); + __mt76_wr(dev, MT_WMM_AIFSN, val); + + val = __mt76_rr(dev, MT_WMM_CWMIN); + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(qid)); + val |= cw_min << MT_WMM_CWMIN_SHIFT(qid); + __mt76_wr(dev, MT_WMM_CWMIN, val); + + val = __mt76_rr(dev, MT_WMM_CWMAX); + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(qid)); + val |= cw_max << MT_WMM_CWMAX_SHIFT(qid); + __mt76_wr(dev, MT_WMM_CWMAX, val); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_conf_tx); + +void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_dev *dev = hw->priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv; + struct ieee80211_sta_rates *rates = rcu_dereference(sta->rates); + struct ieee80211_tx_rate rate = {}; + + if (!rates) + return; + + rate.idx = rates->rate[0].idx; + rate.flags = rates->rate[0].flags; + mt76x02_mac_wcid_set_rate(dev, &msta->wcid, &rate); + + if (dev->drv && dev->drv->get_max_txpwr_adj) + msta->wcid.max_txpwr_adj = dev->drv->get_max_txpwr_adj(dev, &rate); +} +EXPORT_SYMBOL_GPL(mt76x02_sta_rate_tbl_update); + +int mt76x02_insert_hdr_pad(struct sk_buff *skb) +{ + int len = ieee80211_get_hdrlen_from_skb(skb); + + if (len % 4 == 0) + return 0; + + skb_push(skb, 2); + memmove(skb->data, skb->data + 2, len); + + skb->data[len] = 0; + skb->data[len + 1] = 0; + return 2; +} +EXPORT_SYMBOL_GPL(mt76x02_insert_hdr_pad); + +void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len) +{ + int hdrlen; + + if (!len) + return; + + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + memmove(skb->data + len, skb->data, hdrlen); + skb_pull(skb, len); +} +EXPORT_SYMBOL_GPL(mt76x02_remove_hdr_pad); + +static void mt76x02_remove_dma_hdr(struct sk_buff *skb) +{ + int hdr_len; + + skb_pull(skb, sizeof(struct mt76x02_txwi) + MT_DMA_HDR_LEN); + hdr_len = ieee80211_get_hdrlen_from_skb(skb); + if (hdr_len % 4) + mt76x02_remove_hdr_pad(skb, 2); +} + +void mt76x02_tx_complete(struct mt76_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + ieee80211_free_txskb(dev->hw, skb); + } else { + ieee80211_tx_info_clear_status(info); + info->status.rates[0].idx = -1; + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status(dev->hw, skb); + } +} +EXPORT_SYMBOL_GPL(mt76x02_tx_complete); + +void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e, bool flush) +{ + mt76x02_remove_dma_hdr(e->skb); + mt76x02_tx_complete(mdev, e->skb); +} +EXPORT_SYMBOL_GPL(mt76x02_tx_complete_skb); + +bool mt76x02_tx_status_data(struct mt76_dev *dev, u8 *update) +{ + struct mt76x02_tx_status stat; + + if (!mt76x02_mac_load_tx_status(dev, &stat)) + return false; + + mt76x02_send_tx_status(dev, &stat, update); + + return true; +} +EXPORT_SYMBOL_GPL(mt76x02_tx_status_data); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.h b/drivers/net/wireless/mediatek/mt76/mt76x02_util.h new file mode 100644 index 000000000000..2ea9e68bfa3f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> + * + * 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 __MT76X02_UTIL_H +#define __MT76X02_UTIL_H + +extern struct ieee80211_rate mt76x02_rates[12]; + +void mt76x02_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast); +int mt76x02_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int mt76x02_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +void mt76x02_vif_init(struct mt76_dev *dev, struct ieee80211_vif *vif, + unsigned int idx); +int mt76x02_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void mt76x02_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +int mt76x02_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params); +int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int mt76x02_insert_hdr_pad(struct sk_buff *skb); +void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len); +void mt76x02_tx_complete(struct mt76_dev *dev, struct sk_buff *skb); +void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, + struct mt76_queue_entry *e, bool flush); +bool mt76x02_tx_status_data(struct mt76_dev *dev, u8 *update); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2.h index dca3209bf5f1..784962913d9a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h @@ -27,7 +27,6 @@ #include <linux/mutex.h> #include <linux/bitops.h> #include <linux/kfifo.h> -#include <linux/average.h> #define MT7662_FIRMWARE "mt7662.bin" #define MT7662_ROM_PATCH "mt7662_rom_patch.bin" @@ -43,26 +42,11 @@ #define MT_CALIBRATE_INTERVAL HZ -#define MT_MAX_VIFS 8 -#define MT_VIF_WCID(_n) (254 - ((_n) & 7)) - #include "mt76.h" -#include "mt76x2_regs.h" +#include "mt76x02_regs.h" #include "mt76x2_mac.h" #include "mt76x2_dfs.h" -DECLARE_EWMA(signal, 10, 8) - -struct mt76x2_mcu { - struct mutex mutex; - - wait_queue_head_t wait; - struct sk_buff_head res_q; - struct mt76u_buf res_u; - - u32 msg_seq; -}; - struct mt76x2_rx_freq_cal { s8 high_gain[MT_MAX_CHAINS]; s8 rssi_offset[MT_MAX_CHAINS]; @@ -98,15 +82,12 @@ struct mt76x2_dev { struct mutex mutex; const u16 *beacon_offsets; - unsigned long wcid_mask[128 / BITS_PER_LONG]; - int txpower_conf; int txpower_cur; u8 txdone_seq; - DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x2_tx_status); + DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x02_tx_status); - struct mt76x2_mcu mcu; struct sk_buff *rx_head; struct tasklet_struct tx_tasklet; @@ -116,9 +97,6 @@ struct mt76x2_dev { u32 aggr_stats[32]; - struct mt76_wcid global_wcid; - struct mt76_wcid __rcu *wcid[128]; - spinlock_t irq_lock; u32 irqmask; @@ -131,8 +109,6 @@ struct mt76x2_dev { u16 chainmask; - u32 rxfilter; - struct mt76x2_calibration cal; s8 target_power; @@ -146,40 +122,6 @@ struct mt76x2_dev { struct mt76x2_dfs_pattern_detector dfs_pd; }; -struct mt76x2_vif { - u8 idx; - - struct mt76_wcid group_wcid; -}; - -struct mt76x2_sta { - struct mt76_wcid wcid; /* must be first */ - - struct mt76x2_vif *vif; - struct mt76x2_tx_status status; - int n_frames; - - struct ewma_signal rssi; - int inactive_count; -}; - -static inline bool mt76x2_wait_for_mac(struct mt76x2_dev *dev) -{ - int i; - - for (i = 0; i < 500; i++) { - switch (mt76_rr(dev, MT_MAC_CSR0)) { - case 0: - case ~0: - break; - default: - return true; - } - usleep_range(5000, 10000); - } - return false; -} - static inline bool is_mt7612(struct mt76x2_dev *dev) { return mt76_chip(&dev->mt76) == 0x7612; @@ -222,8 +164,6 @@ static inline bool wait_for_wpdma(struct mt76x2_dev *dev) extern const struct ieee80211_ops mt76x2_ops; -extern struct ieee80211_rate mt76x2_rates[12]; - struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev); int mt76x2_register_device(struct mt76x2_dev *dev); void mt76x2_init_debugfs(struct mt76x2_dev *dev); @@ -248,21 +188,16 @@ void mt76x2_phy_set_txpower(struct mt76x2_dev *dev); int mt76x2_mcu_init(struct mt76x2_dev *dev); int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, u8 bw_index, bool scan); -int mt76x2_mcu_set_radio_state(struct mt76x2_dev *dev, bool on); int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level, u8 channel); -int mt76x2_mcu_cleanup(struct mt76x2_dev *dev); int mt76x2_dma_init(struct mt76x2_dev *dev); void mt76x2_dma_cleanup(struct mt76x2_dev *dev); void mt76x2_cleanup(struct mt76x2_dev *dev); -int mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid, - struct sk_buff *skb, int cmd, int seq); void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); -void mt76x2_tx_complete(struct mt76x2_dev *dev, struct sk_buff *skb); int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi, struct sk_buff *skb, struct mt76_queue *q, struct mt76_wcid *wcid, struct ieee80211_sta *sta, @@ -281,43 +216,28 @@ void mt76x2_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps); void mt76x2_update_channel(struct mt76_dev *mdev); -s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev, +s8 mt76x2_tx_get_max_txpwr_adj(struct mt76_dev *dev, const struct ieee80211_tx_rate *rate); s8 mt76x2_tx_get_txpwr_adj(struct mt76x2_dev *dev, s8 txpwr, s8 max_txpwr_adj); void mt76x2_tx_set_txpwr_auto(struct mt76x2_dev *dev, s8 txpwr); -int mt76x2_insert_hdr_pad(struct sk_buff *skb); -bool mt76x2_mac_load_tx_status(struct mt76x2_dev *dev, - struct mt76x2_tx_status *stat); -void mt76x2_send_tx_status(struct mt76x2_dev *dev, - struct mt76x2_tx_status *stat, u8 *update); void mt76x2_reset_wlan(struct mt76x2_dev *dev, bool enable); void mt76x2_init_txpower(struct mt76x2_dev *dev, struct ieee80211_supported_band *sband); void mt76_write_mac_initvals(struct mt76x2_dev *dev); -int mt76x2_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_ampdu_params *params); int mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int mt76x2_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void mt76x2_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); -int mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); int mt76x2_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); -void mt76x2_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast); void mt76x2_txq_init(struct mt76x2_dev *dev, struct ieee80211_txq *txq); -void mt76x2_sta_rate_tbl_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); +void mt76x2_phy_tssi_compensate(struct mt76x2_dev *dev, bool wait); void mt76x2_phy_set_txpower_regs(struct mt76x2_dev *dev, enum nl80211_band band); void mt76x2_configure_tx_delay(struct mt76x2_dev *dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_common.c index a2338ba139b4..3e667d8c0ee7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_common.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_common.c @@ -16,316 +16,7 @@ */ #include "mt76x2.h" - -void mt76x2_txq_init(struct mt76x2_dev *dev, struct ieee80211_txq *txq) -{ - struct mt76_txq *mtxq; - - if (!txq) - return; - - mtxq = (struct mt76_txq *) txq->drv_priv; - if (txq->sta) { - struct mt76x2_sta *sta; - - sta = (struct mt76x2_sta *) txq->sta->drv_priv; - mtxq->wcid = &sta->wcid; - } else { - struct mt76x2_vif *mvif; - - mvif = (struct mt76x2_vif *) txq->vif->drv_priv; - mtxq->wcid = &mvif->group_wcid; - } - - mt76_txq_init(&dev->mt76, txq); -} -EXPORT_SYMBOL_GPL(mt76x2_txq_init); - -int mt76x2_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_ampdu_params *params) -{ - enum ieee80211_ampdu_mlme_action action = params->action; - struct ieee80211_sta *sta = params->sta; - struct mt76x2_dev *dev = hw->priv; - struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; - struct ieee80211_txq *txq = sta->txq[params->tid]; - u16 tid = params->tid; - u16 *ssn = ¶ms->ssn; - struct mt76_txq *mtxq; - - if (!txq) - return -EINVAL; - - mtxq = (struct mt76_txq *)txq->drv_priv; - - switch (action) { - case IEEE80211_AMPDU_RX_START: - mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, *ssn, params->buf_size); - mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); - break; - case IEEE80211_AMPDU_RX_STOP: - mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); - mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, - BIT(16 + tid)); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - mtxq->aggr = true; - mtxq->send_bar = false; - ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); - break; - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - mtxq->aggr = false; - ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); - break; - case IEEE80211_AMPDU_TX_START: - mtxq->agg_ssn = *ssn << 4; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - mtxq->aggr = false; - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - } - - return 0; -} -EXPORT_SYMBOL_GPL(mt76x2_ampdu_action); - -int mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt76x2_dev *dev = hw->priv; - struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; - int ret = 0; - int idx = 0; - int i; - - mutex_lock(&dev->mutex); - - idx = mt76_wcid_alloc(dev->wcid_mask, ARRAY_SIZE(dev->wcid)); - if (idx < 0) { - ret = -ENOSPC; - goto out; - } - - msta->vif = mvif; - msta->wcid.sta = 1; - msta->wcid.idx = idx; - msta->wcid.hw_key_idx = -1; - mt76x2_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); - mt76x2_mac_wcid_set_drop(dev, idx, false); - for (i = 0; i < ARRAY_SIZE(sta->txq); i++) - mt76x2_txq_init(dev, sta->txq[i]); - - if (vif->type == NL80211_IFTYPE_AP) - set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags); - - ewma_signal_init(&msta->rssi); - - rcu_assign_pointer(dev->wcid[idx], &msta->wcid); - -out: - mutex_unlock(&dev->mutex); - - return ret; -} -EXPORT_SYMBOL_GPL(mt76x2_sta_add); - -int mt76x2_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt76x2_dev *dev = hw->priv; - struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; - int idx = msta->wcid.idx; - int i; - - mutex_lock(&dev->mutex); - rcu_assign_pointer(dev->wcid[idx], NULL); - for (i = 0; i < ARRAY_SIZE(sta->txq); i++) - mt76_txq_remove(&dev->mt76, sta->txq[i]); - mt76x2_mac_wcid_set_drop(dev, idx, true); - mt76_wcid_free(dev->wcid_mask, idx); - mt76x2_mac_wcid_setup(dev, idx, 0, NULL); - mutex_unlock(&dev->mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(mt76x2_sta_remove); - -void mt76x2_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mt76x2_dev *dev = hw->priv; - - mt76_txq_remove(&dev->mt76, vif->txq); -} -EXPORT_SYMBOL_GPL(mt76x2_remove_interface); - -int mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct mt76x2_dev *dev = hw->priv; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; - struct mt76x2_sta *msta; - struct mt76_wcid *wcid; - int idx = key->keyidx; - int ret; - - /* fall back to sw encryption for unsupported ciphers */ - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - break; - default: - return -EOPNOTSUPP; - } - - /* - * The hardware does not support per-STA RX GTK, fall back - * to software mode for these. - */ - if ((vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_MESH_POINT) && - (key->cipher == WLAN_CIPHER_SUITE_TKIP || - key->cipher == WLAN_CIPHER_SUITE_CCMP) && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - return -EOPNOTSUPP; - - msta = sta ? (struct mt76x2_sta *) sta->drv_priv : NULL; - wcid = msta ? &msta->wcid : &mvif->group_wcid; - - if (cmd == SET_KEY) { - key->hw_key_idx = wcid->idx; - wcid->hw_key_idx = idx; - if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; - wcid->sw_iv = true; - } - } else { - if (idx == wcid->hw_key_idx) { - wcid->hw_key_idx = -1; - wcid->sw_iv = true; - } - - key = NULL; - } - mt76_wcid_key_setup(&dev->mt76, wcid, key); - - if (!msta) { - if (key || wcid->hw_key_idx == idx) { - ret = mt76x2_mac_wcid_set_key(dev, wcid->idx, key); - if (ret) - return ret; - } - - return mt76x2_mac_shared_key_setup(dev, mvif->idx, idx, key); - } - - return mt76x2_mac_wcid_set_key(dev, msta->wcid.idx, key); -} -EXPORT_SYMBOL_GPL(mt76x2_set_key); - -int mt76x2_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u16 queue, const struct ieee80211_tx_queue_params *params) -{ - struct mt76x2_dev *dev = hw->priv; - u8 cw_min = 5, cw_max = 10, qid; - u32 val; - - qid = dev->mt76.q_tx[queue].hw_idx; - - if (params->cw_min) - cw_min = fls(params->cw_min); - if (params->cw_max) - cw_max = fls(params->cw_max); - - val = FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop) | - FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) | - FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) | - FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max); - mt76_wr(dev, MT_EDCA_CFG_AC(qid), val); - - val = mt76_rr(dev, MT_WMM_TXOP(qid)); - val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(qid)); - val |= params->txop << MT_WMM_TXOP_SHIFT(qid); - mt76_wr(dev, MT_WMM_TXOP(qid), val); - - val = mt76_rr(dev, MT_WMM_AIFSN); - val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(qid)); - val |= params->aifs << MT_WMM_AIFSN_SHIFT(qid); - mt76_wr(dev, MT_WMM_AIFSN, val); - - val = mt76_rr(dev, MT_WMM_CWMIN); - val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(qid)); - val |= cw_min << MT_WMM_CWMIN_SHIFT(qid); - mt76_wr(dev, MT_WMM_CWMIN, val); - - val = mt76_rr(dev, MT_WMM_CWMAX); - val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(qid)); - val |= cw_max << MT_WMM_CWMAX_SHIFT(qid); - mt76_wr(dev, MT_WMM_CWMAX, val); - - return 0; -} -EXPORT_SYMBOL_GPL(mt76x2_conf_tx); - -void mt76x2_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct mt76x2_dev *dev = hw->priv; - u32 flags = 0; - -#define MT76_FILTER(_flag, _hw) do { \ - flags |= *total_flags & FIF_##_flag; \ - dev->rxfilter &= ~(_hw); \ - dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ - } while (0) - - mutex_lock(&dev->mutex); - - dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; - - MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); - MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); - MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | - MT_RX_FILTR_CFG_CTS | - MT_RX_FILTR_CFG_CFEND | - MT_RX_FILTR_CFG_CFACK | - MT_RX_FILTR_CFG_BA | - MT_RX_FILTR_CFG_CTRL_RSV); - MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); - - *total_flags = flags; - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); - - mutex_unlock(&dev->mutex); -} -EXPORT_SYMBOL_GPL(mt76x2_configure_filter); - -void mt76x2_sta_rate_tbl_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mt76x2_dev *dev = hw->priv; - struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; - struct ieee80211_sta_rates *rates = rcu_dereference(sta->rates); - struct ieee80211_tx_rate rate = {}; - - if (!rates) - return; - - rate.idx = rates->rate[0].idx; - rate.flags = rates->rate[0].flags; - mt76x2_mac_wcid_set_rate(dev, &msta->wcid, &rate); - msta->wcid.max_txpwr_adj = mt76x2_tx_get_max_txpwr_adj(dev, &rate); -} -EXPORT_SYMBOL_GPL(mt76x2_sta_rate_tbl_update); +#include "mt76x02_mac.h" void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb) @@ -334,12 +25,13 @@ void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, void *rxwi = skb->data; if (q == MT_RXQ_MCU) { - skb_queue_tail(&dev->mcu.res_q, skb); - wake_up(&dev->mcu.wait); + /* this is used just by mmio code */ + skb_queue_tail(&mdev->mmio.mcu.res_q, skb); + wake_up(&mdev->mmio.mcu.wait); return; } - skb_pull(skb, sizeof(struct mt76x2_rxwi)); + skb_pull(skb, sizeof(struct mt76x02_rxwi)); if (mt76x2_mac_process_rx(dev, skb, rxwi)) { dev_kfree_skb(skb); return; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c index 6720a6a1313f..879ed9138841 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_dma.c @@ -15,37 +15,7 @@ */ #include "mt76x2.h" -#include "mt76x2_dma.h" - -int -mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid, - struct sk_buff *skb, int cmd, int seq) -{ - struct mt76_queue *q = &dev->mt76.q_tx[qid]; - struct mt76_queue_buf buf; - dma_addr_t addr; - u32 tx_info; - - tx_info = MT_MCU_MSG_TYPE_CMD | - FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | - FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | - FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | - FIELD_PREP(MT_MCU_MSG_LEN, skb->len); - - addr = dma_map_single(dev->mt76.dev, skb->data, skb->len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev->mt76.dev, addr)) - return -ENOMEM; - - buf.addr = addr; - buf.len = skb->len; - spin_lock_bh(&q->lock); - mt76_queue_add_buf(dev, q, &buf, 1, tx_info, skb, NULL); - mt76_queue_kick(dev, q); - spin_unlock_bh(&q->lock); - - return 0; -} +#include "mt76x02_dma.h" static int mt76x2_init_tx_queue(struct mt76x2_dev *dev, struct mt76_queue *q, @@ -53,7 +23,7 @@ mt76x2_init_tx_queue(struct mt76x2_dev *dev, struct mt76_queue *q, { int ret; - q->regs = dev->mt76.regs + MT_TX_RING_BASE + idx * MT_RING_SIZE; + q->regs = dev->mt76.mmio.regs + MT_TX_RING_BASE + idx * MT_RING_SIZE; q->ndesc = n_desc; q->hw_idx = idx; @@ -72,7 +42,7 @@ mt76x2_init_rx_queue(struct mt76x2_dev *dev, struct mt76_queue *q, { int ret; - q->regs = dev->mt76.regs + MT_RX_RING_BASE + idx * MT_RING_SIZE; + q->regs = dev->mt76.mmio.regs + MT_RX_RING_BASE + idx * MT_RING_SIZE; q->ndesc = n_desc; q->buf_size = bufsize; @@ -102,32 +72,23 @@ mt76x2_tx_tasklet(unsigned long data) int mt76x2_dma_init(struct mt76x2_dev *dev) { - static const u8 wmm_queue_map[] = { - [IEEE80211_AC_BE] = 0, - [IEEE80211_AC_BK] = 1, - [IEEE80211_AC_VI] = 2, - [IEEE80211_AC_VO] = 3, - }; int ret; int i; struct mt76_txwi_cache __maybe_unused *t; struct mt76_queue *q; - BUILD_BUG_ON(sizeof(t->txwi) < sizeof(struct mt76x2_txwi)); - BUILD_BUG_ON(sizeof(struct mt76x2_rxwi) > MT_RX_HEADROOM); + BUILD_BUG_ON(sizeof(t->txwi) < sizeof(struct mt76x02_txwi)); + BUILD_BUG_ON(sizeof(struct mt76x02_rxwi) > MT_RX_HEADROOM); mt76_dma_attach(&dev->mt76); - init_waitqueue_head(&dev->mcu.wait); - skb_queue_head_init(&dev->mcu.res_q); - tasklet_init(&dev->tx_tasklet, mt76x2_tx_tasklet, (unsigned long) dev); mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); - for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { + for (i = 0; i < IEEE80211_NUM_ACS; i++) { ret = mt76x2_init_tx_queue(dev, &dev->mt76.q_tx[i], - wmm_queue_map[i], MT_TX_RING_SIZE); + mt76_ac_to_hwq(i), MT_TX_RING_SIZE); if (ret) return ret; } @@ -148,7 +109,7 @@ int mt76x2_dma_init(struct mt76x2_dev *dev) return ret; q = &dev->mt76.q_rx[MT_RXQ_MAIN]; - q->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x2_rxwi); + q->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x02_rxwi); ret = mt76x2_init_rx_queue(dev, q, 0, MT76x2_RX_RING_SIZE, MT_RX_BUF_SIZE); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c index b814391f79ac..33f7fabf45c0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c @@ -18,6 +18,7 @@ #include "mt76x2.h" #include "mt76x2_eeprom.h" #include "mt76x2_mcu.h" +#include "mt76x02_util.h" static void mt76x2_mac_pbf_init(struct mt76x2_dev *dev) @@ -101,7 +102,7 @@ static int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard) u32 val; int i, k; - if (!mt76x2_wait_for_mac(dev)) + if (!mt76x02_wait_for_mac(&dev->mt76)) return -ETIMEDOUT; val = mt76_rr(dev, MT_WPDMA_GLO_CFG); @@ -160,14 +161,14 @@ static int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard) mt76_wr(dev, MT_WCID_DROP_BASE + i * 4, 0); for (i = 0; i < 256; i++) - mt76x2_mac_wcid_setup(dev, i, 0, NULL); + mt76x02_mac_wcid_setup(&dev->mt76, i, 0, NULL); for (i = 0; i < MT_MAX_VIFS; i++) - mt76x2_mac_wcid_setup(dev, MT_VIF_WCID(i), i, NULL); + mt76x02_mac_wcid_setup(&dev->mt76, MT_VIF_WCID(i), i, NULL); for (i = 0; i < 16; i++) for (k = 0; k < 4; k++) - mt76x2_mac_shared_key_setup(dev, i, k, NULL); + mt76x02_mac_shared_key_setup(&dev->mt76, i, k, NULL); for (i = 0; i < 8; i++) { mt76x2_mac_set_bssid(dev, i, null_addr); @@ -214,7 +215,7 @@ int mt76x2_mac_start(struct mt76x2_dev *dev) mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX | @@ -377,7 +378,7 @@ int mt76x2_init_hardware(struct mt76x2_dev *dev) if (ret) return ret; - dev->rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); + dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); ret = mt76x2_dma_init(dev); if (ret) @@ -401,7 +402,7 @@ void mt76x2_stop_hardware(struct mt76x2_dev *dev) { cancel_delayed_work_sync(&dev->cal_work); cancel_delayed_work_sync(&dev->mac_work); - mt76x2_mcu_set_radio_state(dev, false); + mt76x02_mcu_set_radio_state(&dev->mt76, false, true); mt76x2_mac_stop(dev, false); } @@ -411,19 +412,20 @@ void mt76x2_cleanup(struct mt76x2_dev *dev) tasklet_disable(&dev->pre_tbtt_tasklet); mt76x2_stop_hardware(dev); mt76x2_dma_cleanup(dev); - mt76x2_mcu_cleanup(dev); + mt76x02_mcu_cleanup(&dev->mt76); } struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev) { static const struct mt76_driver_ops drv_ops = { - .txwi_size = sizeof(struct mt76x2_txwi), + .txwi_size = sizeof(struct mt76x02_txwi), .update_survey = mt76x2_update_channel, .tx_prepare_skb = mt76x2_tx_prepare_skb, .tx_complete_skb = mt76x2_tx_complete_skb, .rx_skb = mt76x2_queue_rx_skb, .rx_poll_complete = mt76x2_rx_poll_complete, .sta_ps = mt76x2_sta_ps, + .get_max_txpwr_adj = mt76x2_tx_get_max_txpwr_adj, }; struct mt76x2_dev *dev; struct mt76_dev *mdev; @@ -435,7 +437,6 @@ struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev) dev = container_of(mdev, struct mt76x2_dev, mt76); mdev->dev = pdev; mdev->drv = &drv_ops; - mutex_init(&dev->mutex); spin_lock_init(&dev->irq_lock); return dev; @@ -534,7 +535,7 @@ int mt76x2_register_device(struct mt76x2_dev *dev) int fifo_size; int i, ret; - fifo_size = roundup_pow_of_two(32 * sizeof(struct mt76x2_tx_status)); + fifo_size = roundup_pow_of_two(32 * sizeof(struct mt76x02_tx_status)); status_fifo = devm_kzalloc(dev->mt76.dev, fifo_size, GFP_KERNEL); if (!status_fifo) return -ENOMEM; @@ -584,8 +585,8 @@ int mt76x2_register_device(struct mt76x2_dev *dev) dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness; dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink; - ret = mt76_register_device(&dev->mt76, true, mt76x2_rates, - ARRAY_SIZE(mt76x2_rates)); + ret = mt76_register_device(&dev->mt76, true, mt76x02_rates, + ARRAY_SIZE(mt76x02_rates)); if (ret) goto fail; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c index 324b2a4b8b67..31de3365cdb8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init_common.c @@ -18,40 +18,6 @@ #include "mt76x2.h" #include "mt76x2_eeprom.h" -#define CCK_RATE(_idx, _rate) { \ - .bitrate = _rate, \ - .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ - .hw_value = (MT_PHY_TYPE_CCK << 8) | _idx, \ - .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + _idx), \ -} - -#define OFDM_RATE(_idx, _rate) { \ - .bitrate = _rate, \ - .hw_value = (MT_PHY_TYPE_OFDM << 8) | _idx, \ - .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | _idx, \ -} - -struct ieee80211_rate mt76x2_rates[] = { - CCK_RATE(0, 10), - CCK_RATE(1, 20), - CCK_RATE(2, 55), - CCK_RATE(3, 110), - OFDM_RATE(0, 60), - OFDM_RATE(1, 90), - OFDM_RATE(2, 120), - OFDM_RATE(3, 180), - OFDM_RATE(4, 240), - OFDM_RATE(5, 360), - OFDM_RATE(6, 480), - OFDM_RATE(7, 540), -}; -EXPORT_SYMBOL_GPL(mt76x2_rates); - -struct mt76x2_reg_pair { - u32 reg; - u32 value; -}; - static void mt76x2_set_wlan_state(struct mt76x2_dev *dev, bool enable) { @@ -93,7 +59,7 @@ EXPORT_SYMBOL_GPL(mt76x2_reset_wlan); static void mt76x2_write_reg_pairs(struct mt76x2_dev *dev, - const struct mt76x2_reg_pair *data, int len) + const struct mt76_reg_pair *data, int len) { while (len > 0) { mt76_wr(dev, data->reg, data->value); @@ -128,7 +94,7 @@ void mt76_write_mac_initvals(struct mt76x2_dev *dev) FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f)) - static const struct mt76x2_reg_pair vals[] = { + static const struct mt76_reg_pair vals[] = { /* Copied from MediaTek reference source */ { MT_PBF_SYS_CTRL, 0x00080c00 }, { MT_PBF_CFG, 0x1efebcff }, @@ -184,7 +150,7 @@ void mt76_write_mac_initvals(struct mt76x2_dev *dev) { MT_PROT_AUTO_TX_CFG, 0x00830083 }, { MT_HT_CTRL_CFG, 0x000001ff }, }; - struct mt76x2_reg_pair prot_vals[] = { + struct mt76_reg_pair prot_vals[] = { { MT_CCK_PROT_CFG, DEFAULT_PROT_CFG_CCK }, { MT_OFDM_PROT_CFG, DEFAULT_PROT_CFG_OFDM }, { MT_MM20_PROT_CFG, DEFAULT_PROT_CFG_20 }, @@ -208,8 +174,8 @@ void mt76x2_init_device(struct mt76x2_dev *dev) hw->max_rate_tries = 1; hw->extra_tx_headroom = 2; - hw->sta_data_size = sizeof(struct mt76x2_sta); - hw->vif_data_size = sizeof(struct mt76x2_vif); + hw->sta_data_size = sizeof(struct mt76x02_sta); + hw->vif_data_size = sizeof(struct mt76x02_vif); ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); @@ -218,8 +184,8 @@ void mt76x2_init_device(struct mt76x2_dev *dev) dev->mt76.sband_5g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; dev->chainmask = 0x202; - dev->global_wcid.idx = 255; - dev->global_wcid.hw_key_idx = -1; + dev->mt76.global_wcid.idx = 255; + dev->mt76.global_wcid.hw_key_idx = -1; dev->slottime = 9; /* init antenna configuration */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c index 23cf437d14f9..241ede98e6d3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c @@ -19,6 +19,7 @@ #include "mt76x2_mcu.h" #include "mt76x2_eeprom.h" #include "mt76x2_trace.h" +#include "mt76x02_util.h" void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr) { @@ -30,7 +31,7 @@ void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr) void mt76x2_mac_poll_tx_status(struct mt76x2_dev *dev, bool irq) { - struct mt76x2_tx_status stat = {}; + struct mt76x02_tx_status stat = {}; unsigned long flags; u8 update = 1; bool ret; @@ -42,7 +43,7 @@ void mt76x2_mac_poll_tx_status(struct mt76x2_dev *dev, bool irq) while (!irq || !kfifo_is_full(&dev->txstatus_fifo)) { spin_lock_irqsave(&dev->irq_lock, flags); - ret = mt76x2_mac_load_tx_status(dev, &stat); + ret = mt76x02_mac_load_tx_status(&dev->mt76, &stat); spin_unlock_irqrestore(&dev->irq_lock, flags); if (!ret) @@ -51,7 +52,7 @@ void mt76x2_mac_poll_tx_status(struct mt76x2_dev *dev, bool irq) trace_mac_txstat_fetch(dev, &stat); if (!irq) { - mt76x2_send_tx_status(dev, &stat, &update); + mt76x02_send_tx_status(&dev->mt76, &stat, &update); continue; } @@ -64,7 +65,7 @@ mt76x2_mac_queue_txdone(struct mt76x2_dev *dev, struct sk_buff *skb, void *txwi_ptr) { struct mt76x2_tx_info *txi = mt76x2_skb_tx_info(skb); - struct mt76x2_txwi *txwi = txwi_ptr; + struct mt76x02_txwi *txwi = txwi_ptr; mt76x2_mac_poll_tx_status(dev, false); @@ -73,16 +74,16 @@ mt76x2_mac_queue_txdone(struct mt76x2_dev *dev, struct sk_buff *skb, txi->wcid = txwi->wcid; txi->pktid = txwi->pktid; trace_mac_txdone_add(dev, txwi->wcid, txwi->pktid); - mt76x2_tx_complete(dev, skb); + mt76x02_tx_complete(&dev->mt76, skb); } void mt76x2_mac_process_tx_status_fifo(struct mt76x2_dev *dev) { - struct mt76x2_tx_status stat; + struct mt76x02_tx_status stat; u8 update = 1; while (kfifo_get(&dev->txstatus_fifo, &stat)) - mt76x2_send_tx_status(dev, &stat, &update); + mt76x02_send_tx_status(&dev->mt76, &stat, &update); } void mt76x2_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, @@ -100,9 +101,9 @@ static int mt76_write_beacon(struct mt76x2_dev *dev, int offset, struct sk_buff *skb) { int beacon_len = dev->beacon_offsets[1] - dev->beacon_offsets[0]; - struct mt76x2_txwi txwi; + struct mt76x02_txwi txwi; - if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x2_txwi))) + if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi))) return -ENOSPC; mt76x2_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h index 5af0107ba748..66a57294fcfc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h @@ -18,22 +18,11 @@ #define __MT76x2_MAC_H #include "mt76.h" +#include "mt76x02_mac.h" struct mt76x2_dev; struct mt76x2_sta; -struct mt76x2_vif; -struct mt76x2_txwi; - -struct mt76x2_tx_status { - u8 valid:1; - u8 success:1; - u8 aggr:1; - u8 ack_req:1; - u8 wcid; - u8 pktid; - u8 retry; - u16 rate; -} __packed __aligned(2); +struct mt76x02_vif; struct mt76x2_tx_info { unsigned long jiffies; @@ -44,111 +33,6 @@ struct mt76x2_tx_info { u8 retry; }; -struct mt76x2_rxwi { - __le32 rxinfo; - - __le32 ctl; - - __le16 tid_sn; - __le16 rate; - - u8 rssi[4]; - - __le32 bbp_rxinfo[4]; -}; - -#define MT_RXINFO_BA BIT(0) -#define MT_RXINFO_DATA BIT(1) -#define MT_RXINFO_NULL BIT(2) -#define MT_RXINFO_FRAG BIT(3) -#define MT_RXINFO_UNICAST BIT(4) -#define MT_RXINFO_MULTICAST BIT(5) -#define MT_RXINFO_BROADCAST BIT(6) -#define MT_RXINFO_MYBSS BIT(7) -#define MT_RXINFO_CRCERR BIT(8) -#define MT_RXINFO_ICVERR BIT(9) -#define MT_RXINFO_MICERR BIT(10) -#define MT_RXINFO_AMSDU BIT(11) -#define MT_RXINFO_HTC BIT(12) -#define MT_RXINFO_RSSI BIT(13) -#define MT_RXINFO_L2PAD BIT(14) -#define MT_RXINFO_AMPDU BIT(15) -#define MT_RXINFO_DECRYPT BIT(16) -#define MT_RXINFO_BSSIDX3 BIT(17) -#define MT_RXINFO_WAPI_KEY BIT(18) -#define MT_RXINFO_PN_LEN GENMASK(21, 19) -#define MT_RXINFO_SW_FTYPE0 BIT(22) -#define MT_RXINFO_SW_FTYPE1 BIT(23) -#define MT_RXINFO_PROBE_RESP BIT(24) -#define MT_RXINFO_BEACON BIT(25) -#define MT_RXINFO_DISASSOC BIT(26) -#define MT_RXINFO_DEAUTH BIT(27) -#define MT_RXINFO_ACTION BIT(28) -#define MT_RXINFO_TCP_SUM_ERR BIT(30) -#define MT_RXINFO_IP_SUM_ERR BIT(31) - -#define MT_RXWI_CTL_WCID GENMASK(7, 0) -#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) -#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) -#define MT_RXWI_CTL_UDF GENMASK(15, 13) -#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16) -#define MT_RXWI_CTL_EOF BIT(31) - -#define MT_RXWI_TID GENMASK(3, 0) -#define MT_RXWI_SN GENMASK(15, 4) - -#define MT_RXWI_RATE_INDEX GENMASK(5, 0) -#define MT_RXWI_RATE_LDPC BIT(6) -#define MT_RXWI_RATE_BW GENMASK(8, 7) -#define MT_RXWI_RATE_SGI BIT(9) -#define MT_RXWI_RATE_STBC BIT(10) -#define MT_RXWI_RATE_LDPC_EXSYM BIT(11) -#define MT_RXWI_RATE_PHY GENMASK(15, 13) - -#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0) -#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4) - -#define MT_TX_PWR_ADJ GENMASK(3, 0) - -enum mt76x2_phy_bandwidth { - MT_PHY_BW_20, - MT_PHY_BW_40, - MT_PHY_BW_80, -}; - -#define MT_TXWI_FLAGS_FRAG BIT(0) -#define MT_TXWI_FLAGS_MMPS BIT(1) -#define MT_TXWI_FLAGS_CFACK BIT(2) -#define MT_TXWI_FLAGS_TS BIT(3) -#define MT_TXWI_FLAGS_AMPDU BIT(4) -#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) -#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) -#define MT_TXWI_FLAGS_NDPS BIT(10) -#define MT_TXWI_FLAGS_RTSBWSIG BIT(11) -#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12) -#define MT_TXWI_FLAGS_SOUND BIT(14) -#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) - -#define MT_TXWI_ACK_CTL_REQ BIT(0) -#define MT_TXWI_ACK_CTL_NSEQ BIT(1) -#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) - -#define MT_TXWI_PKTID_PROBE BIT(7) - -struct mt76x2_txwi { - __le16 flags; - __le16 rate; - u8 ack_ctl; - u8 wcid; - __le16 len_ctl; - __le32 iv; - __le32 eiv; - u8 aid; - u8 txstream; - u8 ctl2; - u8 pktid; -} __packed __aligned(4); - static inline struct mt76x2_tx_info * mt76x2_skb_tx_info(struct sk_buff *skb) { @@ -164,18 +48,9 @@ void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr); int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, void *rxi); -void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi, +void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x02_txwi *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_sta *sta, int len); -void mt76x2_mac_wcid_setup(struct mt76x2_dev *dev, u8 idx, u8 vif_idx, u8 *mac); -int mt76x2_mac_wcid_set_key(struct mt76x2_dev *dev, u8 idx, - struct ieee80211_key_conf *key); -void mt76x2_mac_wcid_set_rate(struct mt76x2_dev *dev, struct mt76_wcid *wcid, - const struct ieee80211_tx_rate *rate); -void mt76x2_mac_wcid_set_drop(struct mt76x2_dev *dev, u8 idx, bool drop); - -int mt76x2_mac_shared_key_setup(struct mt76x2_dev *dev, u8 vif_idx, u8 key_idx, - struct ieee80211_key_conf *key); int mt76x2_mac_set_beacon(struct mt76x2_dev *dev, u8 vif_idx, struct sk_buff *skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac_common.c index 6542644bc325..126650742ba4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac_common.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac_common.c @@ -16,6 +16,7 @@ */ #include "mt76x2.h" +#include "mt76x02_util.h" void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force) { @@ -53,331 +54,14 @@ void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force) } EXPORT_SYMBOL_GPL(mt76x2_mac_stop); -bool mt76x2_mac_load_tx_status(struct mt76x2_dev *dev, - struct mt76x2_tx_status *stat) -{ - u32 stat1, stat2; - - stat2 = mt76_rr(dev, MT_TX_STAT_FIFO_EXT); - stat1 = mt76_rr(dev, MT_TX_STAT_FIFO); - - stat->valid = !!(stat1 & MT_TX_STAT_FIFO_VALID); - if (!stat->valid) - return false; - - stat->success = !!(stat1 & MT_TX_STAT_FIFO_SUCCESS); - stat->aggr = !!(stat1 & MT_TX_STAT_FIFO_AGGR); - stat->ack_req = !!(stat1 & MT_TX_STAT_FIFO_ACKREQ); - stat->wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, stat1); - stat->rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, stat1); - - stat->retry = FIELD_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2); - stat->pktid = FIELD_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2); - - return true; -} -EXPORT_SYMBOL_GPL(mt76x2_mac_load_tx_status); - -static int -mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate, - enum nl80211_band band) -{ - u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); - - txrate->idx = 0; - txrate->flags = 0; - txrate->count = 1; - - switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { - case MT_PHY_TYPE_OFDM: - if (band == NL80211_BAND_2GHZ) - idx += 4; - - txrate->idx = idx; - return 0; - case MT_PHY_TYPE_CCK: - if (idx >= 8) - idx -= 8; - - txrate->idx = idx; - return 0; - case MT_PHY_TYPE_HT_GF: - txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; - /* fall through */ - case MT_PHY_TYPE_HT: - txrate->flags |= IEEE80211_TX_RC_MCS; - txrate->idx = idx; - break; - case MT_PHY_TYPE_VHT: - txrate->flags |= IEEE80211_TX_RC_VHT_MCS; - txrate->idx = idx; - break; - default: - return -EINVAL; - } - - switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { - case MT_PHY_BW_20: - break; - case MT_PHY_BW_40: - txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - break; - case MT_PHY_BW_80: - txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; - break; - default: - return -EINVAL; - } - - if (rate & MT_RXWI_RATE_SGI) - txrate->flags |= IEEE80211_TX_RC_SHORT_GI; - - return 0; -} - -static void -mt76x2_mac_fill_tx_status(struct mt76x2_dev *dev, - struct ieee80211_tx_info *info, - struct mt76x2_tx_status *st, int n_frames) -{ - struct ieee80211_tx_rate *rate = info->status.rates; - int cur_idx, last_rate; - int i; - - if (!n_frames) - return; - - last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); - mt76x2_mac_process_tx_rate(&rate[last_rate], st->rate, - dev->mt76.chandef.chan->band); - if (last_rate < IEEE80211_TX_MAX_RATES - 1) - rate[last_rate + 1].idx = -1; - - cur_idx = rate[last_rate].idx + last_rate; - for (i = 0; i <= last_rate; i++) { - rate[i].flags = rate[last_rate].flags; - rate[i].idx = max_t(int, 0, cur_idx - i); - rate[i].count = 1; - } - rate[last_rate].count = st->retry + 1 - last_rate; - - info->status.ampdu_len = n_frames; - info->status.ampdu_ack_len = st->success ? n_frames : 0; - - if (st->pktid & MT_TXWI_PKTID_PROBE) - info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; - - if (st->aggr) - info->flags |= IEEE80211_TX_CTL_AMPDU | - IEEE80211_TX_STAT_AMPDU; - - if (!st->ack_req) - info->flags |= IEEE80211_TX_CTL_NO_ACK; - else if (st->success) - info->flags |= IEEE80211_TX_STAT_ACK; -} - -void mt76x2_send_tx_status(struct mt76x2_dev *dev, - struct mt76x2_tx_status *stat, u8 *update) -{ - struct ieee80211_tx_info info = {}; - struct ieee80211_sta *sta = NULL; - struct mt76_wcid *wcid = NULL; - struct mt76x2_sta *msta = NULL; - - rcu_read_lock(); - if (stat->wcid < ARRAY_SIZE(dev->wcid)) - wcid = rcu_dereference(dev->wcid[stat->wcid]); - - if (wcid) { - void *priv; - - priv = msta = container_of(wcid, struct mt76x2_sta, wcid); - sta = container_of(priv, struct ieee80211_sta, - drv_priv); - } - - if (msta && stat->aggr) { - u32 stat_val, stat_cache; - - stat_val = stat->rate; - stat_val |= ((u32) stat->retry) << 16; - stat_cache = msta->status.rate; - stat_cache |= ((u32) msta->status.retry) << 16; - - if (*update == 0 && stat_val == stat_cache && - stat->wcid == msta->status.wcid && msta->n_frames < 32) { - msta->n_frames++; - goto out; - } - - mt76x2_mac_fill_tx_status(dev, &info, &msta->status, - msta->n_frames); - - msta->status = *stat; - msta->n_frames = 1; - *update = 0; - } else { - mt76x2_mac_fill_tx_status(dev, &info, stat, 1); - *update = 1; - } - - ieee80211_tx_status_noskb(mt76_hw(dev), sta, &info); - -out: - rcu_read_unlock(); -} -EXPORT_SYMBOL_GPL(mt76x2_send_tx_status); - -static enum mt76x2_cipher_type -mt76x2_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) -{ - memset(key_data, 0, 32); - if (!key) - return MT_CIPHER_NONE; - - if (key->keylen > 32) - return MT_CIPHER_NONE; - - memcpy(key_data, key->key, key->keylen); - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - return MT_CIPHER_WEP40; - case WLAN_CIPHER_SUITE_WEP104: - return MT_CIPHER_WEP104; - case WLAN_CIPHER_SUITE_TKIP: - return MT_CIPHER_TKIP; - case WLAN_CIPHER_SUITE_CCMP: - return MT_CIPHER_AES_CCMP; - default: - return MT_CIPHER_NONE; - } -} - -int mt76x2_mac_shared_key_setup(struct mt76x2_dev *dev, u8 vif_idx, u8 key_idx, - struct ieee80211_key_conf *key) -{ - enum mt76x2_cipher_type cipher; - u8 key_data[32]; - u32 val; - - cipher = mt76x2_mac_get_key_info(key, key_data); - if (cipher == MT_CIPHER_NONE && key) - return -EOPNOTSUPP; - - val = mt76_rr(dev, MT_SKEY_MODE(vif_idx)); - val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); - val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); - mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); - - mt76_wr_copy(dev, MT_SKEY(vif_idx, key_idx), key_data, - sizeof(key_data)); - - return 0; -} -EXPORT_SYMBOL_GPL(mt76x2_mac_shared_key_setup); - -int mt76x2_mac_wcid_set_key(struct mt76x2_dev *dev, u8 idx, - struct ieee80211_key_conf *key) -{ - enum mt76x2_cipher_type cipher; - u8 key_data[32]; - u8 iv_data[8]; - - cipher = mt76x2_mac_get_key_info(key, key_data); - if (cipher == MT_CIPHER_NONE && key) - return -EOPNOTSUPP; - - mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PKEY_MODE, cipher); - mt76_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); - - memset(iv_data, 0, sizeof(iv_data)); - if (key) { - mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE, - !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); - iv_data[3] = key->keyidx << 6; - if (cipher >= MT_CIPHER_TKIP) - iv_data[3] |= 0x20; - } - - mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); - - return 0; -} -EXPORT_SYMBOL_GPL(mt76x2_mac_wcid_set_key); - -static __le16 -mt76x2_mac_tx_rate_val(struct mt76x2_dev *dev, - const struct ieee80211_tx_rate *rate, u8 *nss_val) -{ - u16 rateval; - u8 phy, rate_idx; - u8 nss = 1; - u8 bw = 0; - - if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { - rate_idx = rate->idx; - nss = 1 + (rate->idx >> 4); - phy = MT_PHY_TYPE_VHT; - if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - bw = 2; - else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - bw = 1; - } else if (rate->flags & IEEE80211_TX_RC_MCS) { - rate_idx = rate->idx; - nss = 1 + (rate->idx >> 3); - phy = MT_PHY_TYPE_HT; - if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) - phy = MT_PHY_TYPE_HT_GF; - if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - bw = 1; - } else { - const struct ieee80211_rate *r; - int band = dev->mt76.chandef.chan->band; - u16 val; - - r = &mt76_hw(dev)->wiphy->bands[band]->bitrates[rate->idx]; - if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - val = r->hw_value_short; - else - val = r->hw_value; - - phy = val >> 8; - rate_idx = val & 0xff; - bw = 0; - } - - rateval = FIELD_PREP(MT_RXWI_RATE_INDEX, rate_idx); - rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); - rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); - if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - rateval |= MT_RXWI_RATE_SGI; - - *nss_val = nss; - return cpu_to_le16(rateval); -} - -void mt76x2_mac_wcid_set_rate(struct mt76x2_dev *dev, struct mt76_wcid *wcid, - const struct ieee80211_tx_rate *rate) -{ - spin_lock_bh(&dev->mt76.lock); - wcid->tx_rate = mt76x2_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss); - wcid->tx_rate_set = true; - spin_unlock_bh(&dev->mt76.lock); -} -EXPORT_SYMBOL_GPL(mt76x2_mac_wcid_set_rate); - -void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi, +void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x02_txwi *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_sta *sta, int len) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rate = &info->control.rates[0]; struct ieee80211_key_conf *key = info->control.hw_key; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 rate_ht_mask = FIELD_PREP(MT_RXWI_RATE_PHY, BIT(1) | BIT(2)); - u16 txwi_flags = 0; u8 nss; s8 txpwr_adj, max_txpwr_adj; u8 ccmp_pn[8]; @@ -411,8 +95,8 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi, max_txpwr_adj = wcid->max_txpwr_adj; nss = wcid->tx_rate_nss; } else { - txwi->rate = mt76x2_mac_tx_rate_val(dev, rate, &nss); - max_txpwr_adj = mt76x2_tx_get_max_txpwr_adj(dev, rate); + txwi->rate = mt76x02_mac_tx_rate_val(&dev->mt76, rate, &nss); + max_txpwr_adj = mt76x2_tx_get_max_txpwr_adj(&dev->mt76, rate); } spin_unlock_bh(&dev->mt76.lock); @@ -426,154 +110,10 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi, !(txwi->rate & cpu_to_le16(rate_ht_mask))) txwi->txstream = 0x93; - if (info->flags & IEEE80211_TX_CTL_LDPC) - txwi->rate |= cpu_to_le16(MT_RXWI_RATE_LDPC); - if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1) - txwi->rate |= cpu_to_le16(MT_RXWI_RATE_STBC); - if (nss > 1 && sta && sta->smps_mode == IEEE80211_SMPS_DYNAMIC) - txwi_flags |= MT_TXWI_FLAGS_MMPS; - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ; - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ; - if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) - txwi->pktid |= MT_TXWI_PKTID_PROBE; - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) { - u8 ba_size = IEEE80211_MIN_AMPDU_BUF; - - ba_size <<= sta->ht_cap.ampdu_factor; - ba_size = min_t(int, 63, ba_size - 1); - if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) - ba_size = 0; - txwi->ack_ctl |= FIELD_PREP(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size); - - txwi_flags |= MT_TXWI_FLAGS_AMPDU | - FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, - sta->ht_cap.ampdu_density); - } - - if (ieee80211_is_probe_resp(hdr->frame_control) || - ieee80211_is_beacon(hdr->frame_control)) - txwi_flags |= MT_TXWI_FLAGS_TS; - - txwi->flags |= cpu_to_le16(txwi_flags); - txwi->len_ctl = cpu_to_le16(len); + mt76x02_mac_fill_txwi(txwi, skb, sta, len, nss); } EXPORT_SYMBOL_GPL(mt76x2_mac_write_txwi); -void mt76x2_mac_wcid_set_drop(struct mt76x2_dev *dev, u8 idx, bool drop) -{ - u32 val = mt76_rr(dev, MT_WCID_DROP(idx)); - u32 bit = MT_WCID_DROP_MASK(idx); - - /* prevent unnecessary writes */ - if ((val & bit) != (bit * drop)) - mt76_wr(dev, MT_WCID_DROP(idx), (val & ~bit) | (bit * drop)); -} -EXPORT_SYMBOL_GPL(mt76x2_mac_wcid_set_drop); - -void mt76x2_mac_wcid_setup(struct mt76x2_dev *dev, u8 idx, u8 vif_idx, u8 *mac) -{ - struct mt76_wcid_addr addr = {}; - u32 attr; - - attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | - FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); - - mt76_wr(dev, MT_WCID_ATTR(idx), attr); - - mt76_wr(dev, MT_WCID_TX_RATE(idx), 0); - mt76_wr(dev, MT_WCID_TX_RATE(idx) + 4, 0); - - if (idx >= 128) - return; - - if (mac) - memcpy(addr.macaddr, mac, ETH_ALEN); - - mt76_wr_copy(dev, MT_WCID_ADDR(idx), &addr, sizeof(addr)); -} -EXPORT_SYMBOL_GPL(mt76x2_mac_wcid_setup); - -static int -mt76x2_mac_process_rate(struct mt76_rx_status *status, u16 rate) -{ - u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); - - switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { - case MT_PHY_TYPE_OFDM: - if (idx >= 8) - idx = 0; - - if (status->band == NL80211_BAND_2GHZ) - idx += 4; - - status->rate_idx = idx; - return 0; - case MT_PHY_TYPE_CCK: - if (idx >= 8) { - idx -= 8; - status->enc_flags |= RX_ENC_FLAG_SHORTPRE; - } - - if (idx >= 4) - idx = 0; - - status->rate_idx = idx; - return 0; - case MT_PHY_TYPE_HT_GF: - status->enc_flags |= RX_ENC_FLAG_HT_GF; - /* fall through */ - case MT_PHY_TYPE_HT: - status->encoding = RX_ENC_HT; - status->rate_idx = idx; - break; - case MT_PHY_TYPE_VHT: - status->encoding = RX_ENC_VHT; - status->rate_idx = FIELD_GET(MT_RATE_INDEX_VHT_IDX, idx); - status->nss = FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1; - break; - default: - return -EINVAL; - } - - if (rate & MT_RXWI_RATE_LDPC) - status->enc_flags |= RX_ENC_FLAG_LDPC; - - if (rate & MT_RXWI_RATE_SGI) - status->enc_flags |= RX_ENC_FLAG_SHORT_GI; - - if (rate & MT_RXWI_RATE_STBC) - status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; - - switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { - case MT_PHY_BW_20: - break; - case MT_PHY_BW_40: - status->bw = RATE_INFO_BW_40; - break; - case MT_PHY_BW_80: - status->bw = RATE_INFO_BW_80; - break; - default: - break; - } - - return 0; -} - -static void mt76x2_remove_hdr_pad(struct sk_buff *skb, int len) -{ - int hdrlen; - - if (!len) - return; - - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - memmove(skb->data + len, skb->data, hdrlen); - skb_pull(skb, len); -} - int mt76x2_mac_get_rssi(struct mt76x2_dev *dev, s8 rssi, int chain) { struct mt76x2_rx_freq_cal *cal = &dev->cal.rx; @@ -584,23 +124,23 @@ int mt76x2_mac_get_rssi(struct mt76x2_dev *dev, s8 rssi, int chain) return rssi; } -static struct mt76x2_sta * +static struct mt76x02_sta * mt76x2_rx_get_sta(struct mt76x2_dev *dev, u8 idx) { struct mt76_wcid *wcid; - if (idx >= ARRAY_SIZE(dev->wcid)) + if (idx >= ARRAY_SIZE(dev->mt76.wcid)) return NULL; - wcid = rcu_dereference(dev->wcid[idx]); + wcid = rcu_dereference(dev->mt76.wcid[idx]); if (!wcid) return NULL; - return container_of(wcid, struct mt76x2_sta, wcid); + return container_of(wcid, struct mt76x02_sta, wcid); } static struct mt76_wcid * -mt76x2_rx_get_sta_wcid(struct mt76x2_dev *dev, struct mt76x2_sta *sta, +mt76x2_rx_get_sta_wcid(struct mt76x2_dev *dev, struct mt76x02_sta *sta, bool unicast) { if (!sta) @@ -616,8 +156,8 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, void *rxi) { struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb; - struct mt76x2_rxwi *rxwi = rxi; - struct mt76x2_sta *sta; + struct mt76x02_rxwi *rxwi = rxi; + struct mt76x02_sta *sta; u32 rxinfo = le32_to_cpu(rxwi->rxinfo); u32 ctl = le32_to_cpu(rxwi->ctl); u16 rate = le16_to_cpu(rxwi->rate); @@ -670,7 +210,7 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, } } - mt76x2_remove_hdr_pad(skb, pad_len); + mt76x02_remove_hdr_pad(skb, pad_len); if ((rxinfo & MT_RXINFO_BA) && !(rxinfo & MT_RXINFO_NULL)) status->aggr = true; @@ -694,6 +234,6 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, sta->inactive_count = 0; } - return mt76x2_mac_process_rate(status, rate); + return mt76x02_mac_process_rate(status, rate); } EXPORT_SYMBOL_GPL(mt76x2_mac_process_rx); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c index 680a89f8aa87..7f0a89be154c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c @@ -15,6 +15,7 @@ */ #include "mt76x2.h" +#include "mt76x02_util.h" static int mt76x2_start(struct ieee80211_hw *hw) @@ -22,7 +23,7 @@ mt76x2_start(struct ieee80211_hw *hw) struct mt76x2_dev *dev = hw->priv; int ret; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); ret = mt76x2_mac_start(dev); if (ret) @@ -38,7 +39,7 @@ mt76x2_start(struct ieee80211_hw *hw) set_bit(MT76_STATE_RUNNING, &dev->mt76.state); out: - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return ret; } @@ -47,44 +48,10 @@ mt76x2_stop(struct ieee80211_hw *hw) { struct mt76x2_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); clear_bit(MT76_STATE_RUNNING, &dev->mt76.state); mt76x2_stop_hardware(dev); - mutex_unlock(&dev->mutex); -} - -static int -mt76x2_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct mt76x2_dev *dev = hw->priv; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; - unsigned int idx = 0; - - if (vif->addr[0] & BIT(1)) - idx = 1 + (((dev->mt76.macaddr[0] ^ vif->addr[0]) >> 2) & 7); - - /* - * Client mode typically only has one configurable BSSID register, - * which is used for bssidx=0. This is linked to the MAC address. - * Since mac80211 allows changing interface types, and we cannot - * force the use of the primary MAC address for a station mode - * interface, we need some other way of configuring a per-interface - * remote BSSID. - * The hardware provides an AP-Client feature, where bssidx 0-7 are - * used for AP mode and bssidx 8-15 for client mode. - * We shift the station interface bss index by 8 to force the - * hardware to recognize the BSSID. - * The resulting bssidx mismatch for unicast frames is ignored by hw. - */ - if (vif->type == NL80211_IFTYPE_STATION) - idx += 8; - - mvif->idx = idx; - mvif->group_wcid.idx = MT_VIF_WCID(idx); - mvif->group_wcid.hw_key_idx = -1; - mt76x2_txq_init(dev, vif->txq); - - return 0; + mutex_unlock(&dev->mt76.mutex); } static int @@ -127,15 +94,15 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed) struct mt76x2_dev *dev = hw->priv; int ret = 0; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) - dev->rxfilter |= MT_RX_FILTR_CFG_PROMISC; + dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC; else - dev->rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; + dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -156,7 +123,7 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed) ieee80211_wake_queues(hw); } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return ret; } @@ -166,9 +133,9 @@ mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct mt76x2_dev *dev = hw->priv; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); if (changed & BSS_CHANGED_BSSID) mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid); @@ -195,18 +162,18 @@ mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76x2_set_tx_ackto(dev); } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); } void mt76x2_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) { - struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv; struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); int idx = msta->wcid.idx; mt76_stop_tx_queues(&dev->mt76, sta, true); - mt76x2_mac_wcid_set_drop(dev, idx, ps); + mt76x02_mac_wcid_set_drop(&dev->mt76, idx, ps); } static void @@ -252,10 +219,10 @@ static void mt76x2_set_coverage_class(struct ieee80211_hw *hw, { struct mt76x2_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); dev->coverage_class = coverage_class; mt76x2_set_tx_ackto(dev); - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); } static int @@ -272,7 +239,7 @@ static int mt76x2_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, if (!tx_ant || tx_ant > 3 || tx_ant != rx_ant) return -EINVAL; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); dev->chainmask = (tx_ant == 3) ? 0x202 : 0x101; dev->mt76.antenna_mask = tx_ant; @@ -280,7 +247,7 @@ static int mt76x2_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, mt76_set_stream_caps(&dev->mt76, true); mt76x2_phy_set_antenna(dev); - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return 0; } @@ -290,10 +257,10 @@ static int mt76x2_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, { struct mt76x2_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); *tx_ant = dev->mt76.antenna_mask; *rx_ant = dev->mt76.antenna_mask; - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return 0; } @@ -317,22 +284,22 @@ const struct ieee80211_ops mt76x2_ops = { .tx = mt76x2_tx, .start = mt76x2_start, .stop = mt76x2_stop, - .add_interface = mt76x2_add_interface, - .remove_interface = mt76x2_remove_interface, + .add_interface = mt76x02_add_interface, + .remove_interface = mt76x02_remove_interface, .config = mt76x2_config, - .configure_filter = mt76x2_configure_filter, + .configure_filter = mt76x02_configure_filter, .bss_info_changed = mt76x2_bss_info_changed, - .sta_add = mt76x2_sta_add, - .sta_remove = mt76x2_sta_remove, - .set_key = mt76x2_set_key, - .conf_tx = mt76x2_conf_tx, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + .set_key = mt76x02_set_key, + .conf_tx = mt76x02_conf_tx, .sw_scan_start = mt76x2_sw_scan, .sw_scan_complete = mt76x2_sw_scan_complete, .flush = mt76x2_flush, - .ampdu_action = mt76x2_ampdu_action, + .ampdu_action = mt76x02_ampdu_action, .get_txpower = mt76x2_get_txpower, .wake_tx_queue = mt76_wake_tx_queue, - .sta_rate_tbl_update = mt76x2_sta_rate_tbl_update, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, .release_buffered_frames = mt76_release_buffered_frames, .set_coverage_class = mt76x2_set_coverage_class, .get_survey = mt76_get_survey, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c index 743da57760dc..f92bebfa21fd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c @@ -20,90 +20,14 @@ #include "mt76x2.h" #include "mt76x2_mcu.h" -#include "mt76x2_dma.h" #include "mt76x2_eeprom.h" - -static struct sk_buff *mt76x2_mcu_msg_alloc(const void *data, int len) -{ - struct sk_buff *skb; - - skb = alloc_skb(len, GFP_KERNEL); - if (!skb) - return NULL; - memcpy(skb_put(skb, len), data, len); - - return skb; -} - -static struct sk_buff * -mt76x2_mcu_get_response(struct mt76x2_dev *dev, unsigned long expires) -{ - unsigned long timeout; - - if (!time_is_after_jiffies(expires)) - return NULL; - - timeout = expires - jiffies; - wait_event_timeout(dev->mcu.wait, !skb_queue_empty(&dev->mcu.res_q), - timeout); - return skb_dequeue(&dev->mcu.res_q); -} - -static int -mt76x2_mcu_msg_send(struct mt76x2_dev *dev, struct sk_buff *skb, - enum mcu_cmd cmd) -{ - unsigned long expires = jiffies + HZ; - int ret; - u8 seq; - - if (!skb) - return -EINVAL; - - mutex_lock(&dev->mcu.mutex); - - seq = ++dev->mcu.msg_seq & 0xf; - if (!seq) - seq = ++dev->mcu.msg_seq & 0xf; - - ret = mt76x2_tx_queue_mcu(dev, MT_TXQ_MCU, skb, cmd, seq); - if (ret) - goto out; - - while (1) { - u32 *rxfce; - bool check_seq = false; - - skb = mt76x2_mcu_get_response(dev, expires); - if (!skb) { - dev_err(dev->mt76.dev, - "MCU message %d (seq %d) timed out\n", cmd, - seq); - ret = -ETIMEDOUT; - break; - } - - rxfce = (u32 *) skb->cb; - - if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) - check_seq = true; - - dev_kfree_skb(skb); - if (check_seq) - break; - } - -out: - mutex_unlock(&dev->mcu.mutex); - - return ret; -} +#include "mt76x02_dma.h" static int mt76pci_load_rom_patch(struct mt76x2_dev *dev) { const struct firmware *fw = NULL; - struct mt76x2_patch_header *hdr; + struct mt76x02_patch_header *hdr; bool rom_protect = !is_mt7612(dev); int len, ret = 0; __le32 *cur; @@ -138,7 +62,7 @@ mt76pci_load_rom_patch(struct mt76x2_dev *dev) goto out; } - hdr = (struct mt76x2_patch_header *) fw->data; + hdr = (struct mt76x02_patch_header *)fw->data; dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time); mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ROM_PATCH_OFFSET); @@ -169,7 +93,7 @@ static int mt76pci_load_firmware(struct mt76x2_dev *dev) { const struct firmware *fw; - const struct mt76x2_fw_header *hdr; + const struct mt76x02_fw_header *hdr; int len, ret; __le32 *cur; u32 offset, val; @@ -181,7 +105,7 @@ mt76pci_load_firmware(struct mt76x2_dev *dev) if (!fw || !fw->data || fw->size < sizeof(*hdr)) goto error; - hdr = (const struct mt76x2_fw_header *) fw->data; + hdr = (const struct mt76x02_fw_header *)fw->data; len = sizeof(*hdr); len += le32_to_cpu(hdr->ilm_len); @@ -241,165 +165,15 @@ error: return -ENOENT; } -static int -mt76x2_mcu_function_select(struct mt76x2_dev *dev, enum mcu_function func, - u32 val) -{ - struct sk_buff *skb; - struct { - __le32 id; - __le32 value; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(func), - .value = cpu_to_le32(val), - }; - - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - return mt76x2_mcu_msg_send(dev, skb, CMD_FUN_SET_OP); -} - -int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level, - u8 channel) -{ - struct sk_buff *skb; - struct { - u8 cr_mode; - u8 temp; - u8 ch; - u8 _pad0; - - __le32 cfg; - } __packed __aligned(4) msg = { - .cr_mode = type, - .temp = temp_level, - .ch = channel, - }; - u32 val; - - val = BIT(31); - val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff; - val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00; - msg.cfg = cpu_to_le32(val); - - /* first set the channel without the extension channel info */ - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - return mt76x2_mcu_msg_send(dev, skb, CMD_LOAD_CR); -} - -int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, - u8 bw_index, bool scan) -{ - struct sk_buff *skb; - struct { - u8 idx; - u8 scan; - u8 bw; - u8 _pad0; - - __le16 chainmask; - u8 ext_chan; - u8 _pad1; - - } __packed __aligned(4) msg = { - .idx = channel, - .scan = scan, - .bw = bw, - .chainmask = cpu_to_le16(dev->chainmask), - }; - - /* first set the channel without the extension channel info */ - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - mt76x2_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP); - - usleep_range(5000, 10000); - - msg.ext_chan = 0xe0 + bw_index; - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - return mt76x2_mcu_msg_send(dev, skb, CMD_SWITCH_CHANNEL_OP); -} - -int mt76x2_mcu_set_radio_state(struct mt76x2_dev *dev, bool on) -{ - struct sk_buff *skb; - struct { - __le32 mode; - __le32 level; - } __packed __aligned(4) msg = { - .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), - .level = cpu_to_le32(0), - }; - - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - return mt76x2_mcu_msg_send(dev, skb, CMD_POWER_SAVING_OP); -} - -int mt76x2_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type, - u32 param) -{ - struct sk_buff *skb; - struct { - __le32 id; - __le32 value; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(type), - .value = cpu_to_le32(param), - }; - int ret; - - mt76_clear(dev, MT_MCU_COM_REG0, BIT(31)); - - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - ret = mt76x2_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP); - if (ret) - return ret; - - if (WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0, - BIT(31), BIT(31), 100))) - return -ETIMEDOUT; - - return 0; -} - -int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev, - struct mt76x2_tssi_comp *tssi_data) -{ - struct sk_buff *skb; - struct { - __le32 id; - struct mt76x2_tssi_comp data; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(MCU_CAL_TSSI_COMP), - .data = *tssi_data, - }; - - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - return mt76x2_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP); -} - -int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, - bool force) -{ - struct sk_buff *skb; - struct { - __le32 channel; - __le32 gain_val; - } __packed __aligned(4) msg = { - .channel = cpu_to_le32(channel), - .gain_val = cpu_to_le32(gain), - }; - - if (force) - msg.channel |= cpu_to_le32(BIT(31)); - - skb = mt76x2_mcu_msg_alloc(&msg, sizeof(msg)); - return mt76x2_mcu_msg_send(dev, skb, CMD_INIT_GAIN_OP); -} - int mt76x2_mcu_init(struct mt76x2_dev *dev) { + static const struct mt76_mcu_ops mt76x2_mcu_ops = { + .mcu_msg_alloc = mt76x02_mcu_msg_alloc, + .mcu_send_msg = mt76x02_mcu_msg_send, + }; int ret; - mutex_init(&dev->mcu.mutex); + dev->mt76.mcu_ops = &mt76x2_mcu_ops; ret = mt76pci_load_rom_patch(dev); if (ret) @@ -409,19 +183,6 @@ int mt76x2_mcu_init(struct mt76x2_dev *dev) if (ret) return ret; - mt76x2_mcu_function_select(dev, Q_SELECT, 1); - return 0; -} - -int mt76x2_mcu_cleanup(struct mt76x2_dev *dev) -{ - struct sk_buff *skb; - - mt76_wr(dev, MT_MCU_INT_LEVEL, 1); - usleep_range(20000, 30000); - - while ((skb = skb_dequeue(&dev->mcu.res_q)) != NULL) - dev_kfree_skb(skb); - + mt76x02_mcu_function_select(&dev->mt76, Q_SELECT, 1, true); return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h index e40293f21417..3de062d0b644 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.h @@ -17,15 +17,11 @@ #ifndef __MT76x2_MCU_H #define __MT76x2_MCU_H +#include "mt76x02_mcu.h" + /* Register definitions */ #define MT_MCU_CPU_CTL 0x0704 #define MT_MCU_CLOCK_CTL 0x0708 -#define MT_MCU_RESET_CTL 0x070C -#define MT_MCU_INT_LEVEL 0x0718 -#define MT_MCU_COM_REG0 0x0730 -#define MT_MCU_COM_REG1 0x0734 -#define MT_MCU_COM_REG2 0x0738 -#define MT_MCU_COM_REG3 0x073C #define MT_MCU_PCIE_REMAP_BASE1 0x0740 #define MT_MCU_PCIE_REMAP_BASE2 0x0744 #define MT_MCU_PCIE_REMAP_BASE3 0x0748 @@ -69,47 +65,6 @@ #define MT_MCU_DLM_ADDR 0x90000 #define MT_MCU_DLM_ADDR_E3 0x90800 -enum mcu_cmd { - CMD_FUN_SET_OP = 1, - CMD_LOAD_CR = 2, - CMD_INIT_GAIN_OP = 3, - CMD_DYNC_VGA_OP = 6, - CMD_TDLS_CH_SW = 7, - CMD_BURST_WRITE = 8, - CMD_READ_MODIFY_WRITE = 9, - CMD_RANDOM_READ = 10, - CMD_BURST_READ = 11, - CMD_RANDOM_WRITE = 12, - CMD_LED_MODE_OP = 16, - CMD_POWER_SAVING_OP = 20, - CMD_WOW_CONFIG = 21, - CMD_WOW_QUERY = 22, - CMD_WOW_FEATURE = 24, - CMD_CARRIER_DETECT_OP = 28, - CMD_RADOR_DETECT_OP = 29, - CMD_SWITCH_CHANNEL_OP = 30, - CMD_CALIBRATION_OP = 31, - CMD_BEACON_OP = 32, - CMD_ANTENNA_OP = 33, -}; - -enum mcu_function { - Q_SELECT = 1, - BW_SETTING = 2, - USB2_SW_DISCONNECT = 2, - USB3_SW_DISCONNECT = 3, - LOG_FW_DEBUG_MSG = 4, - GET_FW_VERSION = 5, -}; - -enum mcu_power_mode { - RADIO_OFF = 0x30, - RADIO_ON = 0x31, - RADIO_OFF_AUTO_WAKEUP = 0x32, - RADIO_OFF_ADVANCE = 0x33, - RADIO_ON_ADVANCE = 0x34, -}; - enum mcu_calibration { MCU_CAL_R = 1, MCU_CAL_TEMP_SENSOR, @@ -146,25 +101,6 @@ struct mt76x2_tssi_comp { u8 offset1; } __packed __aligned(4); -struct mt76x2_fw_header { - __le32 ilm_len; - __le32 dlm_len; - __le16 build_ver; - __le16 fw_ver; - u8 pad[4]; - char build_time[16]; -}; - -struct mt76x2_patch_header { - char build_time[16]; - char platform[4]; - char hw_version[4]; - char patch_version[4]; - u8 pad[2]; -}; - -int mt76x2_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type, - u32 param); int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev, struct mt76x2_tssi_comp *tssi_data); int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, bool force); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu_common.c new file mode 100644 index 000000000000..72f6bfb7a258 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu_common.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * 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/kernel.h> +#include <linux/firmware.h> +#include <linux/delay.h> + +#include "mt76x2.h" +#include "mt76x2_mcu.h" +#include "mt76x2_eeprom.h" +#include "mt76x02_dma.h" + +int mt76x2_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, + u8 bw_index, bool scan) +{ + struct sk_buff *skb; + struct { + u8 idx; + u8 scan; + u8 bw; + u8 _pad0; + + __le16 chainmask; + u8 ext_chan; + u8 _pad1; + + } __packed __aligned(4) msg = { + .idx = channel, + .scan = scan, + .bw = bw, + .chainmask = cpu_to_le16(dev->chainmask), + }; + + /* first set the channel without the extension channel info */ + skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg)); + mt76_mcu_send_msg(dev, skb, CMD_SWITCH_CHANNEL_OP, true); + + usleep_range(5000, 10000); + + msg.ext_chan = 0xe0 + bw_index; + skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg)); + return mt76_mcu_send_msg(dev, skb, CMD_SWITCH_CHANNEL_OP, true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_set_channel); + +int mt76x2_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level, + u8 channel) +{ + struct sk_buff *skb; + struct { + u8 cr_mode; + u8 temp; + u8 ch; + u8 _pad0; + + __le32 cfg; + } __packed __aligned(4) msg = { + .cr_mode = type, + .temp = temp_level, + .ch = channel, + }; + u32 val; + + val = BIT(31); + val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff; + val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00; + msg.cfg = cpu_to_le32(val); + + /* first set the channel without the extension channel info */ + skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg)); + return mt76_mcu_send_msg(dev, skb, CMD_LOAD_CR, true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_load_cr); + +int mt76x2_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, + bool force) +{ + struct sk_buff *skb; + struct { + __le32 channel; + __le32 gain_val; + } __packed __aligned(4) msg = { + .channel = cpu_to_le32(channel), + .gain_val = cpu_to_le32(gain), + }; + + if (force) + msg.channel |= cpu_to_le32(BIT(31)); + + skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg)); + return mt76_mcu_send_msg(dev, skb, CMD_INIT_GAIN_OP, true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_init_gain); + +int mt76x2_mcu_tssi_comp(struct mt76x2_dev *dev, + struct mt76x2_tssi_comp *tssi_data) +{ + struct sk_buff *skb; + struct { + __le32 id; + struct mt76x2_tssi_comp data; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(MCU_CAL_TSSI_COMP), + .data = *tssi_data, + }; + + skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg)); + return mt76_mcu_send_msg(dev, skb, CMD_CALIBRATION_OP, true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_tssi_comp); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c index 84c96c0415b6..920bb7c89af9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c @@ -37,7 +37,7 @@ mt76x2_phy_tssi_init_cal(struct mt76x2_dev *dev) if (mt76x2_ext_pa_enabled(dev, chan->band)) flag |= BIT(8); - mt76x2_mcu_calibrate(dev, MCU_CAL_TSSI, flag); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TSSI, flag, true); dev->cal.tssi_cal_done = true; return true; } @@ -61,13 +61,13 @@ mt76x2_phy_channel_calibrate(struct mt76x2_dev *dev, bool mac_stopped) mt76x2_mac_stop(dev, false); if (is_5ghz) - mt76x2_mcu_calibrate(dev, MCU_CAL_LC, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_LC, 0, true); - mt76x2_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz); - mt76x2_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz); - mt76x2_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz); - mt76x2_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0); - mt76x2_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TX_LOFT, is_5ghz, true); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TXIQ, is_5ghz, true); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXIQC_FI, is_5ghz, true); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TEMP_SENSOR, 0, true); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TX_SHAPING, 0, true); if (!mac_stopped) mt76x2_mac_resume(dev); @@ -363,14 +363,14 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev, u8 val = mt76x2_eeprom_get(dev, MT_EE_BT_RCAL_RESULT); if (val != 0xff) - mt76x2_mcu_calibrate(dev, MCU_CAL_R, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_R, 0, true); } - mt76x2_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXDCOC, channel, true); /* Rx LPF calibration */ if (!dev->cal.init_cal_done) - mt76x2_mcu_calibrate(dev, MCU_CAL_RC, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RC, 0, true); dev->cal.init_cal_done = true; @@ -404,47 +404,6 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev, } static void -mt76x2_phy_tssi_compensate(struct mt76x2_dev *dev) -{ - struct ieee80211_channel *chan = dev->mt76.chandef.chan; - struct mt76x2_tx_power_info txp; - struct mt76x2_tssi_comp t = {}; - - if (!dev->cal.tssi_cal_done) - return; - - if (!dev->cal.tssi_comp_pending) { - /* TSSI trigger */ - t.cal_mode = BIT(0); - mt76x2_mcu_tssi_comp(dev, &t); - dev->cal.tssi_comp_pending = true; - } else { - if (mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4)) - return; - - dev->cal.tssi_comp_pending = false; - mt76x2_get_power_info(dev, &txp, chan); - - if (mt76x2_ext_pa_enabled(dev, chan->band)) - t.pa_mode = 1; - - t.cal_mode = BIT(1); - t.slope0 = txp.chain[0].tssi_slope; - t.offset0 = txp.chain[0].tssi_offset; - t.slope1 = txp.chain[1].tssi_slope; - t.offset1 = txp.chain[1].tssi_offset; - mt76x2_mcu_tssi_comp(dev, &t); - - if (t.pa_mode || dev->cal.dpd_cal_done) - return; - - usleep_range(10000, 20000); - mt76x2_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value); - dev->cal.dpd_cal_done = true; - } -} - -static void mt76x2_phy_temp_compensate(struct mt76x2_dev *dev) { struct mt76x2_temp_comp t; @@ -478,7 +437,7 @@ void mt76x2_phy_calibrate(struct work_struct *work) dev = container_of(work, struct mt76x2_dev, cal_work.work); mt76x2_phy_channel_calibrate(dev, false); - mt76x2_phy_tssi_compensate(dev); + mt76x2_phy_tssi_compensate(dev, true); mt76x2_phy_temp_compensate(dev); mt76x2_phy_update_channel_gain(dev); ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, @@ -489,7 +448,7 @@ int mt76x2_phy_start(struct mt76x2_dev *dev) { int ret; - ret = mt76x2_mcu_set_radio_state(dev, true); + ret = mt76x02_mcu_set_radio_state(&dev->mt76, true, true); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c index 9fd6ab4cbb94..3b704a70fad1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy_common.c @@ -17,6 +17,7 @@ #include "mt76x2.h" #include "mt76x2_eeprom.h" +#include "mt76x2_mcu.h" static void mt76x2_adjust_high_lna_gain(struct mt76x2_dev *dev, int reg, s8 offset) @@ -303,7 +304,7 @@ EXPORT_SYMBOL_GPL(mt76x2_phy_set_band); int mt76x2_phy_get_min_avg_rssi(struct mt76x2_dev *dev) { - struct mt76x2_sta *sta; + struct mt76x02_sta *sta; struct mt76_wcid *wcid; int i, j, min_rssi = 0; s8 cur_rssi; @@ -311,8 +312,8 @@ int mt76x2_phy_get_min_avg_rssi(struct mt76x2_dev *dev) local_bh_disable(); rcu_read_lock(); - for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) { - unsigned long mask = dev->wcid_mask[i]; + for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid_mask); i++) { + unsigned long mask = dev->mt76.wcid_mask[i]; if (!mask) continue; @@ -321,11 +322,11 @@ int mt76x2_phy_get_min_avg_rssi(struct mt76x2_dev *dev) if (!(mask & 1)) continue; - wcid = rcu_dereference(dev->wcid[j]); + wcid = rcu_dereference(dev->mt76.wcid[j]); if (!wcid) continue; - sta = container_of(wcid, struct mt76x2_sta, wcid); + sta = container_of(wcid, struct mt76x02_sta, wcid); spin_lock(&dev->mt76.rx_lock); if (sta->inactive_count++ < 5) cur_rssi = ewma_signal_read(&sta->rssi); @@ -347,3 +348,45 @@ int mt76x2_phy_get_min_avg_rssi(struct mt76x2_dev *dev) return min_rssi; } EXPORT_SYMBOL_GPL(mt76x2_phy_get_min_avg_rssi); + +void mt76x2_phy_tssi_compensate(struct mt76x2_dev *dev, bool wait) +{ + struct ieee80211_channel *chan = dev->mt76.chandef.chan; + struct mt76x2_tx_power_info txp; + struct mt76x2_tssi_comp t = {}; + + if (!dev->cal.tssi_cal_done) + return; + + if (!dev->cal.tssi_comp_pending) { + /* TSSI trigger */ + t.cal_mode = BIT(0); + mt76x2_mcu_tssi_comp(dev, &t); + dev->cal.tssi_comp_pending = true; + } else { + if (mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4)) + return; + + dev->cal.tssi_comp_pending = false; + mt76x2_get_power_info(dev, &txp, chan); + + if (mt76x2_ext_pa_enabled(dev, chan->band)) + t.pa_mode = 1; + + t.cal_mode = BIT(1); + t.slope0 = txp.chain[0].tssi_slope; + t.offset0 = txp.chain[0].tssi_offset; + t.slope1 = txp.chain[1].tssi_slope; + t.offset1 = txp.chain[1].tssi_offset; + mt76x2_mcu_tssi_comp(dev, &t); + + if (t.pa_mode || dev->cal.dpd_cal_done) + return; + + usleep_range(10000, 20000); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_DPD, + chan->hw_value, wait); + dev->cal.dpd_cal_done = true; + } +} +EXPORT_SYMBOL_GPL(mt76x2_phy_tssi_compensate); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h b/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h index 4cd424148d4b..eb5afeaefa44 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_trace.h @@ -75,7 +75,7 @@ DEFINE_EVENT(dev_txid_evt, mac_txdone_add, TRACE_EVENT(mac_txstat_fetch, TP_PROTO(struct mt76x2_dev *dev, - struct mt76x2_tx_status *stat), + struct mt76x02_tx_status *stat), TP_ARGS(dev, stat), diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c index 4c907882e8b0..fcdf1879162e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c @@ -15,7 +15,8 @@ */ #include "mt76x2.h" -#include "mt76x2_dma.h" +#include "mt76x02_util.h" +#include "mt76x02_dma.h" struct beacon_bc_data { struct mt76x2_dev *dev; @@ -34,11 +35,11 @@ int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi, int ret; if (q == &dev->mt76.q_tx[MT_TXQ_PSD] && wcid && wcid->idx < 128) - mt76x2_mac_wcid_set_drop(dev, wcid->idx, false); + mt76x02_mac_wcid_set_drop(&dev->mt76, wcid->idx, false); mt76x2_mac_write_txwi(dev, txwi, skb, wcid, sta, skb->len); - ret = mt76x2_insert_hdr_pad(skb); + ret = mt76x02_insert_hdr_pad(skb); if (ret < 0) return ret; @@ -58,7 +59,7 @@ static void mt76x2_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt76x2_dev *dev = (struct mt76x2_dev *) priv; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; struct sk_buff *skb = NULL; if (!(dev->beacon_mask & BIT(mvif->idx))) @@ -76,7 +77,7 @@ mt76x2_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct beacon_bc_data *data = priv; struct mt76x2_dev *dev = data->dev; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; struct ieee80211_tx_info *info; struct sk_buff *skb; @@ -164,7 +165,7 @@ void mt76x2_pre_tbtt_tasklet(unsigned long arg) while ((skb = __skb_dequeue(&data.q)) != NULL) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; - struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *) vif->drv_priv; mt76_dma_tx_queue_skb(&dev->mt76, q, skb, &mvif->group_wcid, NULL); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c b/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c index 36afb166fa3f..dbb3071bed1b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx_common.c @@ -16,7 +16,7 @@ */ #include "mt76x2.h" -#include "mt76x2_dma.h" +#include "dma.h" void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) @@ -24,22 +24,22 @@ void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct mt76x2_dev *dev = hw->priv; struct ieee80211_vif *vif = info->control.vif; - struct mt76_wcid *wcid = &dev->global_wcid; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; if (control->sta) { - struct mt76x2_sta *msta; + struct mt76x02_sta *msta; - msta = (struct mt76x2_sta *)control->sta->drv_priv; + msta = (struct mt76x02_sta *)control->sta->drv_priv; wcid = &msta->wcid; /* sw encrypted frames */ - if (!info->control.hw_key && wcid->hw_key_idx != -1) + if (!info->control.hw_key && wcid->hw_key_idx != 0xff) control->sta = NULL; } if (vif && !control->sta) { - struct mt76x2_vif *mvif; + struct mt76x02_vif *mvif; - mvif = (struct mt76x2_vif *)vif->drv_priv; + mvif = (struct mt76x02_vif *)vif->drv_priv; wcid = &mvif->group_wcid; } @@ -47,25 +47,10 @@ void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, } EXPORT_SYMBOL_GPL(mt76x2_tx); -int mt76x2_insert_hdr_pad(struct sk_buff *skb) -{ - int len = ieee80211_get_hdrlen_from_skb(skb); - - if (len % 4 == 0) - return 0; - - skb_push(skb, 2); - memmove(skb->data, skb->data + 2, len); - - skb->data[len] = 0; - skb->data[len + 1] = 0; - return 2; -} -EXPORT_SYMBOL_GPL(mt76x2_insert_hdr_pad); - -s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev, +s8 mt76x2_tx_get_max_txpwr_adj(struct mt76_dev *mdev, const struct ieee80211_tx_rate *rate) { + struct mt76x2_dev *dev = (struct mt76x2_dev *) mdev; s8 max_txpwr; if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { @@ -131,19 +116,3 @@ void mt76x2_tx_set_txpwr_auto(struct mt76x2_dev *dev, s8 txpwr) MT_PROT_AUTO_TX_CFG_AUTO_PADJ, txpwr_adj); } EXPORT_SYMBOL_GPL(mt76x2_tx_set_txpwr_auto); - -void mt76x2_tx_complete(struct mt76x2_dev *dev, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (info->flags & IEEE80211_TX_CTL_AMPDU) { - ieee80211_free_txskb(mt76_hw(dev), skb); - } else { - ieee80211_tx_info_clear_status(info); - info->status.rates[0].idx = -1; - info->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status(mt76_hw(dev), skb); - } -} -EXPORT_SYMBOL_GPL(mt76x2_tx_complete); - diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2_usb.c index 1428cfdee579..feb5cec66c67 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_usb.c @@ -17,9 +17,11 @@ #include <linux/kernel.h> #include <linux/module.h> +#include "mt76x02_usb.h" #include "mt76x2u.h" static const struct usb_device_id mt76x2u_device_table[] = { + { USB_DEVICE(0x0e8d, 0x7612) }, /* Alfa AWUS036ACM */ { USB_DEVICE(0x0b05, 0x1833) }, /* Asus USB-AC54 */ { USB_DEVICE(0x0b05, 0x17eb) }, /* Asus USB-AC55 */ { USB_DEVICE(0x0b05, 0x180b) }, /* Asus USB-N53 B1 */ @@ -45,6 +47,7 @@ static int mt76x2u_probe(struct usb_interface *intf, udev = usb_get_dev(udev); usb_reset_device(udev); + mt76x02u_init_mcu(&dev->mt76); err = mt76u_init(&dev->mt76, intf); if (err < 0) goto err; @@ -107,16 +110,24 @@ static int __maybe_unused mt76x2u_resume(struct usb_interface *intf) mt76u_mcu_complete_urb, &usb->mcu.cmpl); if (err < 0) - return err; + goto err; err = mt76u_submit_rx_buffers(&dev->mt76); if (err < 0) - return err; + goto err; tasklet_enable(&usb->rx_tasklet); tasklet_enable(&usb->tx_tasklet); - return mt76x2u_init_hardware(dev); + err = mt76x2u_init_hardware(dev); + if (err < 0) + goto err; + + return 0; + +err: + mt76x2u_cleanup(dev); + return err; } MODULE_DEVICE_TABLE(usb, mt76x2u_device_table); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u.h b/drivers/net/wireless/mediatek/mt76/mt76x2u.h index 008092f0cd8a..a0ff6472de1f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u.h @@ -20,8 +20,8 @@ #include <linux/device.h> #include "mt76x2.h" -#include "mt76x2_dma.h" #include "mt76x2_mcu.h" +#include "mt76x02_dma.h" #define MT7612U_EEPROM_SIZE 512 @@ -50,33 +50,18 @@ void mt76x2u_phy_set_txdac(struct mt76x2_dev *dev); void mt76x2u_phy_set_rxpath(struct mt76x2_dev *dev); void mt76x2u_mcu_complete_urb(struct urb *urb); -int mt76x2u_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, - u8 bw_index, bool scan); -int mt76x2u_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type, - u32 val); -int mt76x2u_mcu_tssi_comp(struct mt76x2_dev *dev, - struct mt76x2_tssi_comp *tssi_data); -int mt76x2u_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, - bool force); int mt76x2u_mcu_set_dynamic_vga(struct mt76x2_dev *dev, u8 channel, bool ap, bool ext, int rssi, u32 false_cca); -int mt76x2u_mcu_set_radio_state(struct mt76x2_dev *dev, bool val); -int mt76x2u_mcu_load_cr(struct mt76x2_dev *dev, u8 type, - u8 temp_level, u8 channel); int mt76x2u_mcu_init(struct mt76x2_dev *dev); int mt76x2u_mcu_fw_init(struct mt76x2_dev *dev); -void mt76x2u_mcu_deinit(struct mt76x2_dev *dev); int mt76x2u_alloc_queues(struct mt76x2_dev *dev); void mt76x2u_queues_deinit(struct mt76x2_dev *dev); void mt76x2u_stop_queues(struct mt76x2_dev *dev); -bool mt76x2u_tx_status_data(struct mt76_dev *mdev, u8 *update); int mt76x2u_tx_prepare_skb(struct mt76_dev *mdev, void *data, struct sk_buff *skb, struct mt76_queue *q, struct mt76_wcid *wcid, struct ieee80211_sta *sta, u32 *tx_info); -void mt76x2u_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, - struct mt76_queue_entry *e, bool flush); int mt76x2u_skb_dma_info(struct sk_buff *skb, enum dma_msg_port port, u32 flags); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u_core.c b/drivers/net/wireless/mediatek/mt76/mt76x2u_core.c index 1ca5dd05b265..c2ccdebca470 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u_core.c @@ -16,18 +16,8 @@ #include "mt76x2u.h" #include "dma.h" - -static void mt76x2u_remove_dma_hdr(struct sk_buff *skb) -{ - int hdr_len; - - skb_pull(skb, sizeof(struct mt76x2_txwi) + MT_DMA_HDR_LEN); - hdr_len = ieee80211_get_hdrlen_from_skb(skb); - if (hdr_len % 4) { - memmove(skb->data + 2, skb->data, hdr_len); - skb_pull(skb, 2); - } -} +#include "mt76x02_util.h" +#include "mt76x02_usb.h" static int mt76x2u_check_skb_rooms(struct sk_buff *skb) @@ -35,74 +25,29 @@ mt76x2u_check_skb_rooms(struct sk_buff *skb) int hdr_len = ieee80211_get_hdrlen_from_skb(skb); u32 need_head; - need_head = sizeof(struct mt76x2_txwi) + MT_DMA_HDR_LEN; + need_head = sizeof(struct mt76x02_txwi) + MT_DMA_HDR_LEN; if (hdr_len % 4) need_head += 2; return skb_cow(skb, need_head); } -static int -mt76x2u_set_txinfo(struct sk_buff *skb, - struct mt76_wcid *wcid, u8 ep) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - enum mt76x2_qsel qsel; - u32 flags; - - if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) || - ep == MT_EP_OUT_HCCA) - qsel = MT_QSEL_MGMT; - else - qsel = MT_QSEL_EDCA; - - flags = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) | - MT_TXD_INFO_80211; - if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv) - flags |= MT_TXD_INFO_WIV; - - return mt76u_skb_dma_info(skb, WLAN_PORT, flags); -} - -bool mt76x2u_tx_status_data(struct mt76_dev *mdev, u8 *update) -{ - struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); - struct mt76x2_tx_status stat; - - if (!mt76x2_mac_load_tx_status(dev, &stat)) - return false; - - mt76x2_send_tx_status(dev, &stat, update); - - return true; -} - int mt76x2u_tx_prepare_skb(struct mt76_dev *mdev, void *data, struct sk_buff *skb, struct mt76_queue *q, struct mt76_wcid *wcid, struct ieee80211_sta *sta, u32 *tx_info) { struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); - struct mt76x2_txwi *txwi; + struct mt76x02_txwi *txwi; int err, len = skb->len; err = mt76x2u_check_skb_rooms(skb); if (err < 0) return -ENOMEM; - mt76x2_insert_hdr_pad(skb); + mt76x02_insert_hdr_pad(skb); - txwi = skb_push(skb, sizeof(struct mt76x2_txwi)); + txwi = skb_push(skb, sizeof(struct mt76x02_txwi)); mt76x2_mac_write_txwi(dev, txwi, skb, wcid, sta, len); - return mt76x2u_set_txinfo(skb, wcid, q2ep(q->hw_idx)); + return mt76x02u_set_txinfo(skb, wcid, q2ep(q->hw_idx)); } - -void mt76x2u_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q, - struct mt76_queue_entry *e, bool flush) -{ - struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); - - mt76x2u_remove_dma_hdr(e->skb); - mt76x2_tx_complete(dev, e->skb); -} - diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2u_init.c index 9b81e7641c06..e41880c43fa7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u_init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u_init.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include "mt76x2u.h" +#include "mt76x02_util.h" #include "mt76x2_eeprom.h" static void mt76x2u_init_dma(struct mt76x2_dev *dev) @@ -136,8 +137,8 @@ struct mt76x2_dev *mt76x2u_alloc_device(struct device *pdev) { static const struct mt76_driver_ops drv_ops = { .tx_prepare_skb = mt76x2u_tx_prepare_skb, - .tx_complete_skb = mt76x2u_tx_complete_skb, - .tx_status_data = mt76x2u_tx_status_data, + .tx_complete_skb = mt76x02_tx_complete_skb, + .tx_status_data = mt76x02_tx_status_data, .rx_skb = mt76x2_queue_rx_skb, }; struct mt76x2_dev *dev; @@ -151,8 +152,6 @@ struct mt76x2_dev *mt76x2u_alloc_device(struct device *pdev) mdev->dev = pdev; mdev->drv = &drv_ops; - mutex_init(&dev->mutex); - return dev; } @@ -184,7 +183,7 @@ int mt76x2u_init_hardware(struct mt76x2_dev *dev) mt76x2_reset_wlan(dev, true); mt76x2u_power_on(dev); - if (!mt76x2_wait_for_mac(dev)) + if (!mt76x02_wait_for_mac(&dev->mt76)) return -ETIMEDOUT; err = mt76x2u_mcu_fw_init(dev); @@ -197,7 +196,7 @@ int mt76x2u_init_hardware(struct mt76x2_dev *dev) return -EIO; /* wait for asic ready after fw load. */ - if (!mt76x2_wait_for_mac(dev)) + if (!mt76x02_wait_for_mac(&dev->mt76)) return -ETIMEDOUT; mt76_wr(dev, MT_HEADER_TRANS_CTRL_REG, 0); @@ -214,7 +213,7 @@ int mt76x2u_init_hardware(struct mt76x2_dev *dev) return err; mt76x2u_mac_setaddr(dev, dev->mt76.eeprom.data + MT_EE_MAC_ADDR); - dev->rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); + dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); mt76x2u_init_beacon_offsets(dev); @@ -241,7 +240,7 @@ int mt76x2u_init_hardware(struct mt76x2_dev *dev) mt76_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e); mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x583f); - err = mt76x2u_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0); + err = mt76x2_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0); if (err < 0) return err; @@ -278,8 +277,8 @@ int mt76x2u_register_device(struct mt76x2_dev *dev) wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - err = mt76_register_device(&dev->mt76, true, mt76x2_rates, - ARRAY_SIZE(mt76x2_rates)); + err = mt76_register_device(&dev->mt76, true, mt76x02_rates, + ARRAY_SIZE(mt76x02_rates)); if (err) goto fail; @@ -311,8 +310,8 @@ void mt76x2u_stop_hw(struct mt76x2_dev *dev) void mt76x2u_cleanup(struct mt76x2_dev *dev) { - mt76x2u_mcu_set_radio_state(dev, false); + mt76x02_mcu_set_radio_state(&dev->mt76, false, false); mt76x2u_stop_hw(dev); mt76u_queues_deinit(&dev->mt76); - mt76x2u_mcu_deinit(dev); + mt76u_mcu_deinit(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2u_mac.c index eab7ab297aa6..9604c6a809be 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u_mac.c @@ -122,7 +122,7 @@ int mt76x2u_mac_start(struct mt76x2_dev *dev) wait_for_wpdma(dev); usleep_range(50, 100); - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX | diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2u_main.c index 7367ba111119..c6855549c312 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u_main.c @@ -15,13 +15,14 @@ */ #include "mt76x2u.h" +#include "mt76x02_util.h" static int mt76x2u_start(struct ieee80211_hw *hw) { struct mt76x2_dev *dev = hw->priv; int ret; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); ret = mt76x2u_mac_start(dev); if (ret) @@ -30,7 +31,7 @@ static int mt76x2u_start(struct ieee80211_hw *hw) set_bit(MT76_STATE_RUNNING, &dev->mt76.state); out: - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return ret; } @@ -38,27 +39,21 @@ static void mt76x2u_stop(struct ieee80211_hw *hw) { struct mt76x2_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); clear_bit(MT76_STATE_RUNNING, &dev->mt76.state); mt76x2u_stop_hw(dev); - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); } static int mt76x2u_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mt76x2_dev *dev = hw->priv; - struct mt76x2_vif *mvif = (struct mt76x2_vif *)vif->drv_priv; - unsigned int idx = 0; if (!ether_addr_equal(dev->mt76.macaddr, vif->addr)) mt76x2u_mac_setaddr(dev, vif->addr); - mvif->idx = idx; - mvif->group_wcid.idx = MT_VIF_WCID(idx); - mvif->group_wcid.hw_key_idx = -1; - mt76x2_txq_init(dev, vif->txq); - + mt76x02_vif_init(&dev->mt76, vif, 0); return 0; } @@ -93,7 +88,7 @@ mt76x2u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt76x2_dev *dev = hw->priv; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); if (changed & BSS_CHANGED_ASSOC) { mt76x2u_phy_channel_calibrate(dev); @@ -107,7 +102,7 @@ mt76x2u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, get_unaligned_le16(info->bssid + 4)); } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); } static int @@ -116,14 +111,14 @@ mt76x2u_config(struct ieee80211_hw *hw, u32 changed) struct mt76x2_dev *dev = hw->priv; int err = 0; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) - dev->rxfilter |= MT_RX_FILTR_CFG_PROMISC; + dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC; else - dev->rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; - mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { @@ -142,7 +137,7 @@ mt76x2u_config(struct ieee80211_hw *hw, u32 changed) mt76x2_phy_set_txpower(dev); } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return err; } @@ -169,17 +164,17 @@ const struct ieee80211_ops mt76x2u_ops = { .start = mt76x2u_start, .stop = mt76x2u_stop, .add_interface = mt76x2u_add_interface, - .remove_interface = mt76x2_remove_interface, - .sta_add = mt76x2_sta_add, - .sta_remove = mt76x2_sta_remove, - .set_key = mt76x2_set_key, - .ampdu_action = mt76x2_ampdu_action, + .remove_interface = mt76x02_remove_interface, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + .set_key = mt76x02_set_key, + .ampdu_action = mt76x02_ampdu_action, .config = mt76x2u_config, .wake_tx_queue = mt76_wake_tx_queue, .bss_info_changed = mt76x2u_bss_info_changed, - .configure_filter = mt76x2_configure_filter, - .conf_tx = mt76x2_conf_tx, + .configure_filter = mt76x02_configure_filter, + .conf_tx = mt76x02_conf_tx, .sw_scan_start = mt76x2u_sw_scan, .sw_scan_complete = mt76x2u_sw_scan_complete, - .sta_rate_tbl_update = mt76x2_sta_rate_tbl_update, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2u_mcu.c index 22c16d638baa..fe86b9c696d9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u_mcu.c @@ -18,10 +18,9 @@ #include "mt76x2u.h" #include "mt76x2_eeprom.h" +#include "mt76x02_usb.h" #define MT_CMD_HDR_LEN 4 -#define MT_INBAND_PACKET_MAX_LEN 192 -#define MT_MCU_MEMMAP_WLAN 0x410000 #define MCU_FW_URB_MAX_PAYLOAD 0x3900 #define MCU_ROM_PATCH_MAX_PAYLOAD 2048 @@ -30,150 +29,6 @@ #define MT76U_MCU_DLM_OFFSET 0x110000 #define MT76U_MCU_ROM_PATCH_OFFSET 0x90000 -static int -mt76x2u_mcu_function_select(struct mt76x2_dev *dev, enum mcu_function func, - u32 val) -{ - struct { - __le32 id; - __le32 value; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(func), - .value = cpu_to_le32(val), - }; - struct sk_buff *skb; - - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_FUN_SET_OP, - func != Q_SELECT); -} - -int mt76x2u_mcu_set_radio_state(struct mt76x2_dev *dev, bool val) -{ - struct { - __le32 mode; - __le32 level; - } __packed __aligned(4) msg = { - .mode = cpu_to_le32(val ? RADIO_ON : RADIO_OFF), - .level = cpu_to_le32(0), - }; - struct sk_buff *skb; - - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_POWER_SAVING_OP, - false); -} - -int mt76x2u_mcu_load_cr(struct mt76x2_dev *dev, u8 type, u8 temp_level, - u8 channel) -{ - struct { - u8 cr_mode; - u8 temp; - u8 ch; - u8 _pad0; - __le32 cfg; - } __packed __aligned(4) msg = { - .cr_mode = type, - .temp = temp_level, - .ch = channel, - }; - struct sk_buff *skb; - u32 val; - - val = BIT(31); - val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff; - val |= (mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00; - msg.cfg = cpu_to_le32(val); - - /* first set the channel without the extension channel info */ - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_LOAD_CR, true); -} - -int mt76x2u_mcu_set_channel(struct mt76x2_dev *dev, u8 channel, u8 bw, - u8 bw_index, bool scan) -{ - struct { - u8 idx; - u8 scan; - u8 bw; - u8 _pad0; - - __le16 chainmask; - u8 ext_chan; - u8 _pad1; - - } __packed __aligned(4) msg = { - .idx = channel, - .scan = scan, - .bw = bw, - .chainmask = cpu_to_le16(dev->chainmask), - }; - struct sk_buff *skb; - - /* first set the channel without the extension channel info */ - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - - mt76u_mcu_send_msg(&dev->mt76, skb, CMD_SWITCH_CHANNEL_OP, true); - - usleep_range(5000, 10000); - - msg.ext_chan = 0xe0 + bw_index; - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_SWITCH_CHANNEL_OP, true); -} - -int mt76x2u_mcu_calibrate(struct mt76x2_dev *dev, enum mcu_calibration type, - u32 val) -{ - struct { - __le32 id; - __le32 value; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(type), - .value = cpu_to_le32(val), - }; - struct sk_buff *skb; - - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_CALIBRATION_OP, true); -} - -int mt76x2u_mcu_init_gain(struct mt76x2_dev *dev, u8 channel, u32 gain, - bool force) -{ - struct { - __le32 channel; - __le32 gain_val; - } __packed __aligned(4) msg = { - .channel = cpu_to_le32(channel), - .gain_val = cpu_to_le32(gain), - }; - struct sk_buff *skb; - - if (force) - msg.channel |= cpu_to_le32(BIT(31)); - - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_INIT_GAIN_OP, true); -} - int mt76x2u_mcu_set_dynamic_vga(struct mt76x2_dev *dev, u8 channel, bool ap, bool ext, int rssi, u32 false_cca) { @@ -194,28 +49,8 @@ int mt76x2u_mcu_set_dynamic_vga(struct mt76x2_dev *dev, u8 channel, bool ap, val |= BIT(30); msg.channel = cpu_to_le32(val); - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_DYNC_VGA_OP, true); -} - -int mt76x2u_mcu_tssi_comp(struct mt76x2_dev *dev, - struct mt76x2_tssi_comp *tssi_data) -{ - struct { - __le32 id; - struct mt76x2_tssi_comp data; - } __packed __aligned(4) msg = { - .id = cpu_to_le32(MCU_CAL_TSSI_COMP), - .data = *tssi_data, - }; - struct sk_buff *skb; - - skb = mt76u_mcu_msg_alloc(&msg, sizeof(msg)); - if (!skb) - return -ENOMEM; - return mt76u_mcu_send_msg(&dev->mt76, skb, CMD_CALIBRATION_OP, true); + skb = mt76_mcu_msg_alloc(dev, &msg, sizeof(msg)); + return mt76_mcu_send_msg(dev, skb, CMD_DYNC_VGA_OP, true); } static void mt76x2u_mcu_load_ivb(struct mt76x2_dev *dev) @@ -257,7 +92,7 @@ static void mt76x2u_mcu_reset_wmt(struct mt76x2_dev *dev) static int mt76x2u_mcu_load_rom_patch(struct mt76x2_dev *dev) { bool rom_protect = !is_mt7612(dev); - struct mt76x2_patch_header *hdr; + struct mt76x02_patch_header *hdr; u32 val, patch_mask, patch_reg; const struct firmware *fw; int err; @@ -292,7 +127,7 @@ static int mt76x2u_mcu_load_rom_patch(struct mt76x2_dev *dev) goto out; } - hdr = (struct mt76x2_patch_header *)fw->data; + hdr = (struct mt76x02_patch_header *)fw->data; dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time); /* enable USB_DMA_CFG */ @@ -302,7 +137,7 @@ static int mt76x2u_mcu_load_rom_patch(struct mt76x2_dev *dev) mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val); /* vendor reset */ - mt76u_mcu_fw_reset(&dev->mt76); + mt76x02u_mcu_fw_reset(&dev->mt76); usleep_range(5000, 10000); /* enable FCE to send in-band cmd */ @@ -316,10 +151,10 @@ static int mt76x2u_mcu_load_rom_patch(struct mt76x2_dev *dev) /* FCE skip_fs_en */ mt76_wr(dev, MT_FCE_SKIP_FS, 0x3); - err = mt76u_mcu_fw_send_data(&dev->mt76, fw->data + sizeof(*hdr), - fw->size - sizeof(*hdr), - MCU_ROM_PATCH_MAX_PAYLOAD, - MT76U_MCU_ROM_PATCH_OFFSET); + err = mt76x02u_mcu_fw_send_data(&dev->mt76, fw->data + sizeof(*hdr), + fw->size - sizeof(*hdr), + MCU_ROM_PATCH_MAX_PAYLOAD, + MT76U_MCU_ROM_PATCH_OFFSET); if (err < 0) { err = -EIO; goto out; @@ -344,7 +179,7 @@ out: static int mt76x2u_mcu_load_firmware(struct mt76x2_dev *dev) { u32 val, dlm_offset = MT76U_MCU_DLM_OFFSET; - const struct mt76x2_fw_header *hdr; + const struct mt76x02_fw_header *hdr; int err, len, ilm_len, dlm_len; const struct firmware *fw; @@ -357,7 +192,7 @@ static int mt76x2u_mcu_load_firmware(struct mt76x2_dev *dev) goto out; } - hdr = (const struct mt76x2_fw_header *)fw->data; + hdr = (const struct mt76x02_fw_header *)fw->data; ilm_len = le32_to_cpu(hdr->ilm_len); dlm_len = le32_to_cpu(hdr->dlm_len); len = sizeof(*hdr) + ilm_len + dlm_len; @@ -375,7 +210,7 @@ static int mt76x2u_mcu_load_firmware(struct mt76x2_dev *dev) dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time); /* vendor reset */ - mt76u_mcu_fw_reset(&dev->mt76); + mt76x02u_mcu_fw_reset(&dev->mt76); usleep_range(5000, 10000); /* enable USB_DMA_CFG */ @@ -395,9 +230,9 @@ static int mt76x2u_mcu_load_firmware(struct mt76x2_dev *dev) mt76_wr(dev, MT_FCE_SKIP_FS, 0x3); /* load ILM */ - err = mt76u_mcu_fw_send_data(&dev->mt76, fw->data + sizeof(*hdr), - ilm_len, MCU_FW_URB_MAX_PAYLOAD, - MT76U_MCU_ILM_OFFSET); + err = mt76x02u_mcu_fw_send_data(&dev->mt76, fw->data + sizeof(*hdr), + ilm_len, MCU_FW_URB_MAX_PAYLOAD, + MT76U_MCU_ILM_OFFSET); if (err < 0) { err = -EIO; goto out; @@ -406,10 +241,10 @@ static int mt76x2u_mcu_load_firmware(struct mt76x2_dev *dev) /* load DLM */ if (mt76xx_rev(dev) >= MT76XX_REV_E3) dlm_offset += 0x800; - err = mt76u_mcu_fw_send_data(&dev->mt76, - fw->data + sizeof(*hdr) + ilm_len, - dlm_len, MCU_FW_URB_MAX_PAYLOAD, - dlm_offset); + err = mt76x02u_mcu_fw_send_data(&dev->mt76, + fw->data + sizeof(*hdr) + ilm_len, + dlm_len, MCU_FW_URB_MAX_PAYLOAD, + dlm_offset); if (err < 0) { err = -EIO; goto out; @@ -447,17 +282,10 @@ int mt76x2u_mcu_init(struct mt76x2_dev *dev) { int err; - err = mt76x2u_mcu_function_select(dev, Q_SELECT, 1); + err = mt76x02_mcu_function_select(&dev->mt76, Q_SELECT, + 1, false); if (err < 0) return err; - return mt76x2u_mcu_set_radio_state(dev, true); -} - -void mt76x2u_mcu_deinit(struct mt76x2_dev *dev) -{ - struct mt76_usb *usb = &dev->mt76.usb; - - usb_kill_urb(usb->mcu.res.urb); - mt76u_buf_free(&usb->mcu.res); + return mt76x02_mcu_set_radio_state(&dev->mt76, true, false); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2u_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2u_phy.c index 5158063d0c2e..97f40fef5559 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2u_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2u_phy.c @@ -61,58 +61,17 @@ void mt76x2u_phy_channel_calibrate(struct mt76x2_dev *dev) mt76x2u_mac_stop(dev); if (is_5ghz) - mt76x2u_mcu_calibrate(dev, MCU_CAL_LC, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_LC, 0, false); - mt76x2u_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz); - mt76x2u_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz); - mt76x2u_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz); - mt76x2u_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TX_LOFT, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TXIQ, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXIQC_FI, is_5ghz, false); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TEMP_SENSOR, 0, false); mt76x2u_mac_resume(dev); } static void -mt76x2u_phy_tssi_compensate(struct mt76x2_dev *dev) -{ - struct ieee80211_channel *chan = dev->mt76.chandef.chan; - struct mt76x2_tx_power_info txp; - struct mt76x2_tssi_comp t = {}; - - if (!dev->cal.tssi_cal_done) - return; - - if (!dev->cal.tssi_comp_pending) { - /* TSSI trigger */ - t.cal_mode = BIT(0); - mt76x2u_mcu_tssi_comp(dev, &t); - dev->cal.tssi_comp_pending = true; - } else { - if (mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4)) - return; - - dev->cal.tssi_comp_pending = false; - mt76x2_get_power_info(dev, &txp, chan); - - if (mt76x2_ext_pa_enabled(dev, chan->band)) - t.pa_mode = 1; - - t.cal_mode = BIT(1); - t.slope0 = txp.chain[0].tssi_slope; - t.offset0 = txp.chain[0].tssi_offset; - t.slope1 = txp.chain[1].tssi_slope; - t.offset1 = txp.chain[1].tssi_offset; - mt76x2u_mcu_tssi_comp(dev, &t); - - if (t.pa_mode || dev->cal.dpd_cal_done) - return; - - usleep_range(10000, 20000); - mt76x2u_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value); - dev->cal.dpd_cal_done = true; - } -} - -static void mt76x2u_phy_update_channel_gain(struct mt76x2_dev *dev) { u8 channel = dev->mt76.chandef.chan->hw_value; @@ -155,7 +114,7 @@ void mt76x2u_phy_calibrate(struct work_struct *work) struct mt76x2_dev *dev; dev = container_of(work, struct mt76x2_dev, cal_work.work); - mt76x2u_phy_tssi_compensate(dev); + mt76x2_phy_tssi_compensate(dev, false); mt76x2u_phy_update_channel_gain(dev); ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, @@ -239,11 +198,11 @@ int mt76x2u_phy_set_channel(struct mt76x2_dev *dev, MT_EXT_CCA_CFG_CCA_MASK), ext_cca_chan[ch_group_index]); - ret = mt76x2u_mcu_set_channel(dev, channel, bw, bw_index, scan); + ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan); if (ret) return ret; - mt76x2u_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true); + mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true); /* Enable LDPC Rx */ if (mt76xx_rev(dev) >= MT76XX_REV_E3) @@ -253,14 +212,15 @@ int mt76x2u_phy_set_channel(struct mt76x2_dev *dev, u8 val = mt76x2_eeprom_get(dev, MT_EE_BT_RCAL_RESULT); if (val != 0xff) - mt76x2u_mcu_calibrate(dev, MCU_CAL_R, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_R, + 0, false); } - mt76x2u_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RXDCOC, channel, false); /* Rx LPF calibration */ if (!dev->cal.init_cal_done) - mt76x2u_mcu_calibrate(dev, MCU_CAL_RC, 0); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_RC, 0, false); dev->cal.init_cal_done = true; mt76_wr(dev, MT_BBP(AGC, 61), 0xff64a4e2); @@ -292,7 +252,8 @@ int mt76x2u_phy_set_channel(struct mt76x2_dev *dev, flag |= BIT(0); if (mt76x2_ext_pa_enabled(dev, chan->band)) flag |= BIT(8); - mt76x2u_mcu_calibrate(dev, MCU_CAL_TSSI, flag); + mt76x02_mcu_calibrate(&dev->mt76, MCU_CAL_TSSI, + flag, false); dev->cal.tssi_cal_done = true; } } diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index af48d43bb7dc..cf79b8c67b52 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -442,3 +442,19 @@ void mt76_txq_init(struct mt76_dev *dev, struct ieee80211_txq *txq) mtxq->hwq = &dev->q_tx[mt76_txq_get_qid(txq)]; } EXPORT_SYMBOL_GPL(mt76_txq_init); + +u8 mt76_ac_to_hwq(u8 ac) +{ + static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, + }; + + if (WARN_ON(ac >= IEEE80211_NUM_ACS)) + return 0; + + return wmm_queue_map[ac]; +} +EXPORT_SYMBOL_GPL(mt76_ac_to_hwq); diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 7780b07543bb..be43e2941dc4 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -109,6 +109,7 @@ u32 mt76u_rr(struct mt76_dev *dev, u32 addr) return ret; } +EXPORT_SYMBOL_GPL(mt76u_rr); /* should be called with usb_ctrl_mtx locked */ static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) @@ -140,6 +141,7 @@ void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) __mt76u_wr(dev, addr, val); mutex_unlock(&dev->usb.usb_ctrl_mtx); } +EXPORT_SYMBOL_GPL(mt76u_wr); static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val) @@ -187,6 +189,60 @@ void mt76u_single_wr(struct mt76_dev *dev, const u8 req, EXPORT_SYMBOL_GPL(mt76u_single_wr); static int +mt76u_req_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + + mutex_lock(&usb->usb_ctrl_mtx); + while (len > 0) { + __mt76u_wr(dev, base + data->reg, data->value); + len--; + data++; + } + mutex_unlock(&usb->usb_ctrl_mtx); + + return 0; +} + +static int +mt76u_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int n) +{ + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->state)) + return dev->mcu_ops->mcu_wr_rp(dev, base, data, n); + else + return mt76u_req_wr_rp(dev, base, data, n); +} + +static int +mt76u_req_rd_rp(struct mt76_dev *dev, u32 base, struct mt76_reg_pair *data, + int len) +{ + struct mt76_usb *usb = &dev->usb; + + mutex_lock(&usb->usb_ctrl_mtx); + while (len > 0) { + data->value = __mt76u_rr(dev, base + data->reg); + len--; + data++; + } + mutex_unlock(&usb->usb_ctrl_mtx); + + return 0; +} + +static int +mt76u_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int n) +{ + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->state)) + return dev->mcu_ops->mcu_rd_rp(dev, base, data, n); + else + return mt76u_req_rd_rp(dev, base, data, n); +} + +static int mt76u_set_endpoints(struct usb_interface *intf, struct mt76_usb *usb) { @@ -219,6 +275,7 @@ static int mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf, int nsgs, int len, int sglen) { + struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; struct urb *urb = buf->urb; int i; @@ -227,7 +284,7 @@ mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76u_buf *buf, void *data; int offset; - data = netdev_alloc_frag(len); + data = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC); if (!data) break; @@ -494,10 +551,18 @@ static int mt76u_alloc_rx(struct mt76_dev *dev) static void mt76u_free_rx(struct mt76_dev *dev) { struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; + struct page *page; int i; for (i = 0; i < q->ndesc; i++) mt76u_buf_free(&q->entry[i].ubuf); + + if (!q->rx_page.va) + return; + + page = virt_to_page(q->rx_page.va); + __page_frag_cache_drain(page, q->rx_page.pagecnt_bias); + memset(&q->rx_page, 0, sizeof(q->rx_page)); } static void mt76u_stop_rx(struct mt76_dev *dev) @@ -509,40 +574,6 @@ static void mt76u_stop_rx(struct mt76_dev *dev) usb_kill_urb(q->entry[i].ubuf.urb); } -int mt76u_skb_dma_info(struct sk_buff *skb, int port, u32 flags) -{ - struct sk_buff *iter, *last = skb; - u32 info, pad; - - /* Buffer layout: - * | 4B | xfer len | pad | 4B | - * | TXINFO | pkt/cmd | zero pad to 4B | zero | - * - * length field of TXINFO should be set to 'xfer len'. - */ - info = FIELD_PREP(MT_TXD_INFO_LEN, round_up(skb->len, 4)) | - FIELD_PREP(MT_TXD_INFO_DPORT, port) | flags; - put_unaligned_le32(info, skb_push(skb, sizeof(info))); - - pad = round_up(skb->len, 4) + 4 - skb->len; - skb_walk_frags(skb, iter) { - last = iter; - if (!iter->next) { - skb->data_len += pad; - skb->len += pad; - break; - } - } - - if (unlikely(pad)) { - if (__skb_pad(last, pad, true)) - return -ENOMEM; - __skb_put(last, pad); - } - return 0; -} -EXPORT_SYMBOL_GPL(mt76u_skb_dma_info); - static void mt76u_tx_tasklet(unsigned long data) { struct mt76_dev *dev = (struct mt76_dev *)data; @@ -715,7 +746,7 @@ static int mt76u_alloc_tx(struct mt76_dev *dev) q = &dev->q_tx[i]; spin_lock_init(&q->lock); INIT_LIST_HEAD(&q->swq); - q->hw_idx = q2hwq(i); + q->hw_idx = mt76_ac_to_hwq(i); q->entry = devm_kzalloc(dev->dev, MT_NUM_TX_ENTRIES * sizeof(*q->entry), @@ -822,6 +853,8 @@ int mt76u_init(struct mt76_dev *dev, .wr = mt76u_wr, .rmw = mt76u_rmw, .copy = mt76u_copy, + .wr_rp = mt76u_wr_rp, + .rd_rp = mt76u_rd_rp, }; struct mt76_usb *usb = &dev->usb; diff --git a/drivers/net/wireless/mediatek/mt76/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/usb_mcu.c index 070be803d463..036be4163e69 100644 --- a/drivers/net/wireless/mediatek/mt76/usb_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/usb_mcu.c @@ -14,32 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/firmware.h> - #include "mt76.h" -#include "dma.h" - -#define MT_CMD_HDR_LEN 4 - -#define MT_FCE_DMA_ADDR 0x0230 -#define MT_FCE_DMA_LEN 0x0234 - -#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 - -struct sk_buff *mt76u_mcu_msg_alloc(const void *data, int len) -{ - struct sk_buff *skb; - - skb = alloc_skb(MT_CMD_HDR_LEN + len + 8, GFP_KERNEL); - if (!skb) - return NULL; - - skb_reserve(skb, MT_CMD_HDR_LEN); - skb_put_data(skb, data, len); - - return skb; -} -EXPORT_SYMBOL_GPL(mt76u_mcu_msg_alloc); void mt76u_mcu_complete_urb(struct urb *urb) { @@ -49,176 +24,6 @@ void mt76u_mcu_complete_urb(struct urb *urb) } EXPORT_SYMBOL_GPL(mt76u_mcu_complete_urb); -static int mt76u_mcu_wait_resp(struct mt76_dev *dev, u8 seq) -{ - struct mt76_usb *usb = &dev->usb; - struct mt76u_buf *buf = &usb->mcu.res; - int i, ret; - u32 rxfce; - - for (i = 0; i < 5; i++) { - if (!wait_for_completion_timeout(&usb->mcu.cmpl, - msecs_to_jiffies(300))) - continue; - - if (buf->urb->status) - return -EIO; - - rxfce = get_unaligned_le32(sg_virt(&buf->urb->sg[0])); - ret = mt76u_submit_buf(dev, USB_DIR_IN, - MT_EP_IN_CMD_RESP, - buf, GFP_KERNEL, - mt76u_mcu_complete_urb, - &usb->mcu.cmpl); - if (ret) - return ret; - - if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce)) - return 0; - - dev_err(dev->dev, "error: MCU resp evt:%lx seq:%hhx-%lx\n", - FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce), - seq, FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce)); - } - - dev_err(dev->dev, "error: %s timed out\n", __func__); - return -ETIMEDOUT; -} - -int mt76u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb, - int cmd, bool wait_resp) -{ - struct usb_interface *intf = to_usb_interface(dev->dev); - struct usb_device *udev = interface_to_usbdev(intf); - struct mt76_usb *usb = &dev->usb; - unsigned int pipe; - int ret, sent; - u8 seq = 0; - u32 info; - - if (test_bit(MT76_REMOVED, &dev->state)) - return 0; - - mutex_lock(&usb->mcu.mutex); - - pipe = usb_sndbulkpipe(udev, usb->out_ep[MT_EP_OUT_INBAND_CMD]); - if (wait_resp) { - seq = ++usb->mcu.msg_seq & 0xf; - if (!seq) - seq = ++usb->mcu.msg_seq & 0xf; - } - - info = FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | - FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | - MT_MCU_MSG_TYPE_CMD; - ret = mt76u_skb_dma_info(skb, CPU_TX_PORT, info); - if (ret) - goto out; - - ret = usb_bulk_msg(udev, pipe, skb->data, skb->len, &sent, 500); - if (ret) - goto out; - - if (wait_resp) - ret = mt76u_mcu_wait_resp(dev, seq); - -out: - mutex_unlock(&usb->mcu.mutex); - - consume_skb(skb); - - return ret; -} -EXPORT_SYMBOL_GPL(mt76u_mcu_send_msg); - -void mt76u_mcu_fw_reset(struct mt76_dev *dev) -{ - mt76u_vendor_request(dev, MT_VEND_DEV_MODE, - USB_DIR_OUT | USB_TYPE_VENDOR, - 0x1, 0, NULL, 0); -} -EXPORT_SYMBOL_GPL(mt76u_mcu_fw_reset); - -static int -__mt76u_mcu_fw_send_data(struct mt76_dev *dev, struct mt76u_buf *buf, - const void *fw_data, int len, u32 dst_addr) -{ - u8 *data = sg_virt(&buf->urb->sg[0]); - DECLARE_COMPLETION_ONSTACK(cmpl); - __le32 info; - u32 val; - int err; - - info = cpu_to_le32(FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | - FIELD_PREP(MT_MCU_MSG_LEN, len) | - MT_MCU_MSG_TYPE_CMD); - - memcpy(data, &info, sizeof(info)); - memcpy(data + sizeof(info), fw_data, len); - memset(data + sizeof(info) + len, 0, 4); - - mt76u_single_wr(dev, MT_VEND_WRITE_FCE, - MT_FCE_DMA_ADDR, dst_addr); - len = roundup(len, 4); - mt76u_single_wr(dev, MT_VEND_WRITE_FCE, - MT_FCE_DMA_LEN, len << 16); - - buf->len = MT_CMD_HDR_LEN + len + sizeof(info); - err = mt76u_submit_buf(dev, USB_DIR_OUT, - MT_EP_OUT_INBAND_CMD, - buf, GFP_KERNEL, - mt76u_mcu_complete_urb, &cmpl); - if (err < 0) - return err; - - if (!wait_for_completion_timeout(&cmpl, - msecs_to_jiffies(1000))) { - dev_err(dev->dev, "firmware upload timed out\n"); - usb_kill_urb(buf->urb); - return -ETIMEDOUT; - } - - if (mt76u_urb_error(buf->urb)) { - dev_err(dev->dev, "firmware upload failed: %d\n", - buf->urb->status); - return buf->urb->status; - } - - val = mt76u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); - val++; - mt76u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); - - return 0; -} - -int mt76u_mcu_fw_send_data(struct mt76_dev *dev, const void *data, - int data_len, u32 max_payload, u32 offset) -{ - int err, len, pos = 0, max_len = max_payload - 8; - struct mt76u_buf buf; - - err = mt76u_buf_alloc(dev, &buf, 1, max_payload, max_payload, - GFP_KERNEL); - if (err < 0) - return err; - - while (data_len > 0) { - len = min_t(int, data_len, max_len); - err = __mt76u_mcu_fw_send_data(dev, &buf, data + pos, - len, offset + pos); - if (err < 0) - break; - - data_len -= len; - pos += len; - usleep_range(5000, 10000); - } - mt76u_buf_free(&buf); - - return err; -} -EXPORT_SYMBOL_GPL(mt76u_mcu_fw_send_data); - int mt76u_mcu_init_rx(struct mt76_dev *dev) { struct mt76_usb *usb = &dev->usb; @@ -240,3 +45,12 @@ int mt76u_mcu_init_rx(struct mt76_dev *dev) return err; } EXPORT_SYMBOL_GPL(mt76u_mcu_init_rx); + +void mt76u_mcu_deinit(struct mt76_dev *dev) +{ + struct mt76_usb *usb = &dev->usb; + + usb_kill_urb(usb->mcu.res.urb); + mt76u_buf_free(&usb->mcu.res); +} +EXPORT_SYMBOL_GPL(mt76u_mcu_deinit); diff --git a/drivers/net/wireless/quantenna/qtnfmac/Makefile b/drivers/net/wireless/quantenna/qtnfmac/Makefile index 97f760a3d599..17cd7adb4109 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/Makefile +++ b/drivers/net/wireless/quantenna/qtnfmac/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_QTNFMAC_PEARL_PCIE) += qtnfmac_pearl_pcie.o qtnfmac_pearl_pcie-objs += \ shm_ipc.o \ - pearl/pcie.o + pcie/pcie.o \ + pcie/pearl_pcie.o qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o diff --git a/drivers/net/wireless/quantenna/qtnfmac/bus.h b/drivers/net/wireless/quantenna/qtnfmac/bus.h index 323e47cea1e2..528ca7f5e070 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/bus.h +++ b/drivers/net/wireless/quantenna/qtnfmac/bus.h @@ -20,6 +20,9 @@ #include <linux/netdevice.h> #include <linux/workqueue.h> +#include "trans.h" +#include "core.h" + #define QTNF_MAX_MAC 3 enum qtnf_fw_state { @@ -57,10 +60,8 @@ struct qtnf_bus { struct qtnf_wmac *mac[QTNF_MAX_MAC]; struct qtnf_qlink_transport trans; struct qtnf_hw_info hw_info; - char fwname[32]; struct napi_struct mux_napi; struct net_device mux_dev; - struct completion firmware_init_complete; struct workqueue_struct *workqueue; struct work_struct fw_work; struct work_struct event_work; diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 4aa332f4646b..452d4b7c832d 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -691,11 +691,8 @@ qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_chan_def *chandef = &wdev->chandef; struct ieee80211_channel *chan; struct qtnf_chan_stats stats; - struct qtnf_vif *vif; int ret; - vif = qtnf_netdev_get_priv(dev); - sband = wiphy->bands[NL80211_BAND_2GHZ]; if (sband && idx >= sband->n_channels) { idx -= sband->n_channels; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c new file mode 100644 index 000000000000..d1637f2354a6 --- /dev/null +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2018 Quantenna Communications, Inc. All rights reserved. */ + +#include <linux/printk.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/netdevice.h> +#include <linux/seq_file.h> +#include <linux/workqueue.h> +#include <linux/completion.h> + +#include "pcie_priv.h" +#include "bus.h" +#include "shm_ipc.h" +#include "core.h" +#include "debug.h" + +#undef pr_fmt +#define pr_fmt(fmt) "qtnf_pcie: %s: " fmt, __func__ + +#define QTN_SYSCTL_BAR 0 +#define QTN_SHMEM_BAR 2 +#define QTN_DMA_BAR 3 + +int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb) +{ + struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + int ret; + + ret = qtnf_shm_ipc_send(&priv->shm_ipc_ep_in, skb->data, skb->len); + + if (ret == -ETIMEDOUT) { + pr_err("EP firmware is dead\n"); + bus->fw_state = QTNF_FW_STATE_EP_DEAD; + } + + return ret; +} + +int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv) +{ + struct sk_buff **vaddr; + int len; + + len = priv->tx_bd_num * sizeof(*priv->tx_skb) + + priv->rx_bd_num * sizeof(*priv->rx_skb); + vaddr = devm_kzalloc(&priv->pdev->dev, len, GFP_KERNEL); + + if (!vaddr) + return -ENOMEM; + + priv->tx_skb = vaddr; + + vaddr += priv->tx_bd_num; + priv->rx_skb = vaddr; + + return 0; +} + +void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus) +{ + struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + struct pci_dev *pdev = priv->pdev; + + get_device(&pdev->dev); + schedule_work(&bus->fw_work); +} + +static int qtnf_dbg_mps_show(struct seq_file *s, void *data) +{ + struct qtnf_bus *bus = dev_get_drvdata(s->private); + struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + + seq_printf(s, "%d\n", priv->mps); + + return 0; +} + +static int qtnf_dbg_msi_show(struct seq_file *s, void *data) +{ + struct qtnf_bus *bus = dev_get_drvdata(s->private); + struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + + seq_printf(s, "%u\n", priv->msi_enabled); + + return 0; +} + +static int qtnf_dbg_shm_stats(struct seq_file *s, void *data) +{ + struct qtnf_bus *bus = dev_get_drvdata(s->private); + struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + + seq_printf(s, "shm_ipc_ep_in.tx_packet_count(%zu)\n", + priv->shm_ipc_ep_in.tx_packet_count); + seq_printf(s, "shm_ipc_ep_in.rx_packet_count(%zu)\n", + priv->shm_ipc_ep_in.rx_packet_count); + seq_printf(s, "shm_ipc_ep_out.tx_packet_count(%zu)\n", + priv->shm_ipc_ep_out.tx_timeout_count); + seq_printf(s, "shm_ipc_ep_out.rx_packet_count(%zu)\n", + priv->shm_ipc_ep_out.rx_packet_count); + + return 0; +} + +void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success, + const char *drv_name) +{ + struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + struct pci_dev *pdev = priv->pdev; + int ret; + + if (boot_success) { + bus->fw_state = QTNF_FW_STATE_FW_DNLD_DONE; + + ret = qtnf_core_attach(bus); + if (ret) { + pr_err("failed to attach core\n"); + boot_success = false; + } + } + + if (boot_success) { + qtnf_debugfs_init(bus, drv_name); + qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show); + qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show); + qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats); + } else { + bus->fw_state = QTNF_FW_STATE_DETACHED; + } + + put_device(&pdev->dev); +} + +static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv) +{ + struct pci_dev *pdev = priv->pdev; + struct pci_dev *parent; + int mps_p, mps_o, mps_m, mps; + int ret; + + /* current mps */ + mps_o = pcie_get_mps(pdev); + + /* maximum supported mps */ + mps_m = 128 << pdev->pcie_mpss; + + /* suggested new mps value */ + mps = mps_m; + + if (pdev->bus && pdev->bus->self) { + /* parent (bus) mps */ + parent = pdev->bus->self; + + if (pci_is_pcie(parent)) { + mps_p = pcie_get_mps(parent); + mps = min(mps_m, mps_p); + } + } + + ret = pcie_set_mps(pdev, mps); + if (ret) { + pr_err("failed to set mps to %d, keep using current %d\n", + mps, mps_o); + priv->mps = mps_o; + return; + } + + pr_debug("set mps to %d (was %d, max %d)\n", mps, mps_o, mps_m); + priv->mps = mps; +} + +static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi) +{ + struct pci_dev *pdev = priv->pdev; + + /* fall back to legacy INTx interrupts by default */ + priv->msi_enabled = 0; + + /* check if MSI capability is available */ + if (use_msi) { + if (!pci_enable_msi(pdev)) { + pr_debug("enabled MSI interrupt\n"); + priv->msi_enabled = 1; + } else { + pr_warn("failed to enable MSI interrupts"); + } + } + + if (!priv->msi_enabled) { + pr_warn("legacy PCIE interrupts enabled\n"); + pci_intx(pdev, 1); + } +} + +static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index) +{ + void __iomem *vaddr; + dma_addr_t busaddr; + size_t len; + int ret; + + ret = pcim_iomap_regions(priv->pdev, 1 << index, "qtnfmac_pcie"); + if (ret) + return IOMEM_ERR_PTR(ret); + + busaddr = pci_resource_start(priv->pdev, index); + len = pci_resource_len(priv->pdev, index); + vaddr = pcim_iomap_table(priv->pdev)[index]; + if (!vaddr) + return IOMEM_ERR_PTR(-ENOMEM); + + pr_debug("BAR%u vaddr=0x%p busaddr=%pad len=%u\n", + index, vaddr, &busaddr, (int)len); + + return vaddr; +} + +static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv) +{ + int ret = -ENOMEM; + + priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR); + if (IS_ERR(priv->sysctl_bar)) { + pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR); + return ret; + } + + priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR); + if (IS_ERR(priv->dmareg_bar)) { + pr_err("failed to map BAR%u\n", QTN_DMA_BAR); + return ret; + } + + priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR); + if (IS_ERR(priv->epmem_bar)) { + pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR); + return ret; + } + + return 0; +} + +static void qtnf_pcie_control_rx_callback(void *arg, const u8 *buf, size_t len) +{ + struct qtnf_pcie_bus_priv *priv = arg; + struct qtnf_bus *bus = pci_get_drvdata(priv->pdev); + struct sk_buff *skb; + + if (unlikely(len == 0)) { + pr_warn("zero length packet received\n"); + return; + } + + skb = __dev_alloc_skb(len, GFP_KERNEL); + + if (unlikely(!skb)) { + pr_err("failed to allocate skb\n"); + return; + } + + skb_put_data(skb, buf, len); + + qtnf_trans_handle_rx_ctl_packet(bus, skb); +} + +void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv, + struct qtnf_shm_ipc_region __iomem *ipc_tx_reg, + struct qtnf_shm_ipc_region __iomem *ipc_rx_reg, + const struct qtnf_shm_ipc_int *ipc_int) +{ + const struct qtnf_shm_ipc_rx_callback rx_callback = { + qtnf_pcie_control_rx_callback, priv }; + + qtnf_shm_ipc_init(&priv->shm_ipc_ep_in, QTNF_SHM_IPC_OUTBOUND, + ipc_tx_reg, priv->workqueue, + ipc_int, &rx_callback); + qtnf_shm_ipc_init(&priv->shm_ipc_ep_out, QTNF_SHM_IPC_INBOUND, + ipc_rx_reg, priv->workqueue, + ipc_int, &rx_callback); +} + +int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size, + const struct qtnf_bus_ops *bus_ops, u64 dma_mask, + bool use_msi) +{ + struct qtnf_pcie_bus_priv *pcie_priv; + struct qtnf_bus *bus; + int ret; + + bus = devm_kzalloc(&pdev->dev, + sizeof(*bus) + priv_size, GFP_KERNEL); + if (!bus) + return -ENOMEM; + + pcie_priv = get_bus_priv(bus); + + pci_set_drvdata(pdev, bus); + bus->bus_ops = bus_ops; + bus->dev = &pdev->dev; + bus->fw_state = QTNF_FW_STATE_RESET; + pcie_priv->pdev = pdev; + pcie_priv->tx_stopped = 0; + + mutex_init(&bus->bus_lock); + spin_lock_init(&pcie_priv->tx_lock); + spin_lock_init(&pcie_priv->tx_reclaim_lock); + + pcie_priv->tx_full_count = 0; + pcie_priv->tx_done_count = 0; + pcie_priv->pcie_irq_count = 0; + pcie_priv->tx_reclaim_done = 0; + pcie_priv->tx_reclaim_req = 0; + + pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE"); + if (!pcie_priv->workqueue) { + pr_err("failed to alloc bus workqueue\n"); + ret = -ENODEV; + goto err_init; + } + + init_dummy_netdev(&bus->mux_dev); + + if (!pci_is_pcie(pdev)) { + pr_err("device %s is not PCI Express\n", pci_name(pdev)); + ret = -EIO; + goto err_base; + } + + qtnf_tune_pcie_mps(pcie_priv); + + ret = pcim_enable_device(pdev); + if (ret) { + pr_err("failed to init PCI device %x\n", pdev->device); + goto err_base; + } else { + pr_debug("successful init of PCI device %x\n", pdev->device); + } + + ret = dma_set_mask_and_coherent(&pdev->dev, dma_mask); + if (ret) { + pr_err("PCIE DMA coherent mask init failed\n"); + goto err_base; + } + + pci_set_master(pdev); + qtnf_pcie_init_irq(pcie_priv, use_msi); + + ret = qtnf_pcie_init_memory(pcie_priv); + if (ret < 0) { + pr_err("PCIE memory init failed\n"); + goto err_base; + } + + pci_save_state(pdev); + + return 0; + +err_base: + flush_workqueue(pcie_priv->workqueue); + destroy_workqueue(pcie_priv->workqueue); +err_init: + pci_set_drvdata(pdev, NULL); + + return ret; +} + +static void qtnf_pcie_free_shm_ipc(struct qtnf_pcie_bus_priv *priv) +{ + qtnf_shm_ipc_free(&priv->shm_ipc_ep_in); + qtnf_shm_ipc_free(&priv->shm_ipc_ep_out); +} + +void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv) +{ + cancel_work_sync(&bus->fw_work); + + if (bus->fw_state == QTNF_FW_STATE_ACTIVE || + bus->fw_state == QTNF_FW_STATE_EP_DEAD) + qtnf_core_detach(bus); + + netif_napi_del(&bus->mux_napi); + flush_workqueue(priv->workqueue); + destroy_workqueue(priv->workqueue); + tasklet_kill(&priv->reclaim_tq); + + qtnf_pcie_free_shm_ipc(priv); + qtnf_debugfs_remove(bus); + pci_set_drvdata(priv->pdev, NULL); +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h new file mode 100644 index 000000000000..5c70fb4c0f92 --- /dev/null +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2018 Quantenna Communications, Inc. All rights reserved. */ + +#ifndef _QTN_FMAC_PCIE_H_ +#define _QTN_FMAC_PCIE_H_ + +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/skbuff.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> + +#include "shm_ipc.h" +#include "bus.h" + +#define SKB_BUF_SIZE 2048 + +#define QTN_FW_DL_TIMEOUT_MS 3000 +#define QTN_FW_QLINK_TIMEOUT_MS 30000 +#define QTN_EP_RESET_WAIT_MS 1000 + +struct qtnf_pcie_bus_priv { + struct pci_dev *pdev; + + spinlock_t tx_reclaim_lock; + spinlock_t tx_lock; + int mps; + + struct workqueue_struct *workqueue; + struct tasklet_struct reclaim_tq; + + void __iomem *sysctl_bar; + void __iomem *epmem_bar; + void __iomem *dmareg_bar; + + struct qtnf_shm_ipc shm_ipc_ep_in; + struct qtnf_shm_ipc shm_ipc_ep_out; + + u16 tx_bd_num; + u16 rx_bd_num; + + struct sk_buff **tx_skb; + struct sk_buff **rx_skb; + + u32 rx_bd_w_index; + u32 rx_bd_r_index; + + u32 tx_bd_w_index; + u32 tx_bd_r_index; + + /* diagnostics stats */ + u32 pcie_irq_count; + u32 tx_full_count; + u32 tx_done_count; + u32 tx_reclaim_done; + u32 tx_reclaim_req; + + u8 msi_enabled; + u8 tx_stopped; +}; + +int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb); +int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv); +void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus); +void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success, + const char *drv_name); +void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv, + struct qtnf_shm_ipc_region __iomem *ipc_tx_reg, + struct qtnf_shm_ipc_region __iomem *ipc_rx_reg, + const struct qtnf_shm_ipc_int *ipc_int); +int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size, + const struct qtnf_bus_ops *bus_ops, u64 dma_mask, + bool use_msi); +void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv); + +static inline void qtnf_non_posted_write(u32 val, void __iomem *basereg) +{ + writel(val, basereg); + + /* flush posted write */ + readl(basereg); +} + +#endif /* _QTN_FMAC_PCIE_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c new file mode 100644 index 000000000000..5aca12a51fe3 --- /dev/null +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c @@ -0,0 +1,1262 @@ +/* + * Copyright (c) 2015-2016 Quantenna Communications, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/crc32.h> +#include <linux/spinlock.h> +#include <linux/circ_buf.h> +#include <linux/log2.h> + +#include "pcie_priv.h" +#include "pearl_pcie_regs.h" +#include "pearl_pcie_ipc.h" +#include "qtn_hw_ids.h" +#include "core.h" +#include "bus.h" +#include "shm_ipc.h" +#include "debug.h" + +static bool use_msi = true; +module_param(use_msi, bool, 0644); +MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt"); + +static unsigned int tx_bd_size_param = 32; +module_param(tx_bd_size_param, uint, 0644); +MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two"); + +static unsigned int rx_bd_size_param = 256; +module_param(rx_bd_size_param, uint, 0644); +MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two"); + +static u8 flashboot = 1; +module_param(flashboot, byte, 0644); +MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS"); + +#define DRV_NAME "qtnfmac_pearl_pcie" + +struct qtnf_pearl_bda { + __le16 bda_len; + __le16 bda_version; + __le32 bda_pci_endian; + __le32 bda_ep_state; + __le32 bda_rc_state; + __le32 bda_dma_mask; + __le32 bda_msi_addr; + __le32 bda_flashsz; + u8 bda_boardname[PCIE_BDA_NAMELEN]; + __le32 bda_rc_msi_enabled; + u8 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; + __le32 bda_dsbw_start_index; + __le32 bda_dsbw_end_index; + __le32 bda_dsbw_total_bytes; + __le32 bda_rc_tx_bd_base; + __le32 bda_rc_tx_bd_num; + u8 bda_pcie_mac[QTN_ENET_ADDR_LENGTH]; + struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096); /* host TX */ + struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096); /* host RX */ +} __packed; + +struct qtnf_pearl_tx_bd { + __le32 addr; + __le32 addr_h; + __le32 info; + __le32 info_h; +} __packed; + +struct qtnf_pearl_rx_bd { + __le32 addr; + __le32 addr_h; + __le32 info; + __le32 info_h; + __le32 next_ptr; + __le32 next_ptr_h; +} __packed; + +struct qtnf_pearl_fw_hdr { + u8 boardflg[8]; + __le32 fwsize; + __le32 seqnum; + __le32 type; + __le32 pktlen; + __le32 crc; +} __packed; + +struct qtnf_pcie_pearl_state { + struct qtnf_pcie_bus_priv base; + + /* lock for irq configuration changes */ + spinlock_t irq_lock; + + struct qtnf_pearl_bda __iomem *bda; + void __iomem *pcie_reg_base; + + struct qtnf_pearl_tx_bd *tx_bd_vbase; + dma_addr_t tx_bd_pbase; + + struct qtnf_pearl_rx_bd *rx_bd_vbase; + dma_addr_t rx_bd_pbase; + + dma_addr_t bd_table_paddr; + void *bd_table_vaddr; + u32 bd_table_len; + u32 pcie_irq_mask; + u32 pcie_irq_rx_count; + u32 pcie_irq_tx_count; + u32 pcie_irq_uf_count; +}; + +static inline void qtnf_init_hdp_irqs(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + ps->pcie_irq_mask = (PCIE_HDP_INT_RX_BITS | PCIE_HDP_INT_TX_BITS); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static inline void qtnf_enable_hdp_irqs(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + writel(ps->pcie_irq_mask, PCIE_HDP_INT_EN(ps->pcie_reg_base)); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static inline void qtnf_disable_hdp_irqs(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + writel(0x0, PCIE_HDP_INT_EN(ps->pcie_reg_base)); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static inline void qtnf_en_rxdone_irq(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + ps->pcie_irq_mask |= PCIE_HDP_INT_RX_BITS; + writel(ps->pcie_irq_mask, PCIE_HDP_INT_EN(ps->pcie_reg_base)); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static inline void qtnf_dis_rxdone_irq(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + ps->pcie_irq_mask &= ~PCIE_HDP_INT_RX_BITS; + writel(ps->pcie_irq_mask, PCIE_HDP_INT_EN(ps->pcie_reg_base)); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static inline void qtnf_en_txdone_irq(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + ps->pcie_irq_mask |= PCIE_HDP_INT_TX_BITS; + writel(ps->pcie_irq_mask, PCIE_HDP_INT_EN(ps->pcie_reg_base)); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static inline void qtnf_dis_txdone_irq(struct qtnf_pcie_pearl_state *ps) +{ + unsigned long flags; + + spin_lock_irqsave(&ps->irq_lock, flags); + ps->pcie_irq_mask &= ~PCIE_HDP_INT_TX_BITS; + writel(ps->pcie_irq_mask, PCIE_HDP_INT_EN(ps->pcie_reg_base)); + spin_unlock_irqrestore(&ps->irq_lock, flags); +} + +static void qtnf_deassert_intx(struct qtnf_pcie_pearl_state *ps) +{ + void __iomem *reg = ps->base.sysctl_bar + PEARL_PCIE_CFG0_OFFSET; + u32 cfg; + + cfg = readl(reg); + cfg &= ~PEARL_ASSERT_INTX; + qtnf_non_posted_write(cfg, reg); +} + +static void qtnf_pearl_reset_ep(struct qtnf_pcie_pearl_state *ps) +{ + const u32 data = QTN_PEARL_IPC_IRQ_WORD(QTN_PEARL_LHOST_EP_RESET); + void __iomem *reg = ps->base.sysctl_bar + + QTN_PEARL_SYSCTL_LHOST_IRQ_OFFSET; + + qtnf_non_posted_write(data, reg); + msleep(QTN_EP_RESET_WAIT_MS); + pci_restore_state(ps->base.pdev); +} + +static void qtnf_pcie_pearl_ipc_gen_ep_int(void *arg) +{ + const struct qtnf_pcie_pearl_state *ps = arg; + const u32 data = QTN_PEARL_IPC_IRQ_WORD(QTN_PEARL_LHOST_IPC_IRQ); + void __iomem *reg = ps->base.sysctl_bar + + QTN_PEARL_SYSCTL_LHOST_IRQ_OFFSET; + + qtnf_non_posted_write(data, reg); +} + +static int qtnf_is_state(__le32 __iomem *reg, u32 state) +{ + u32 s = readl(reg); + + return s & state; +} + +static void qtnf_set_state(__le32 __iomem *reg, u32 state) +{ + u32 s = readl(reg); + + qtnf_non_posted_write(state | s, reg); +} + +static void qtnf_clear_state(__le32 __iomem *reg, u32 state) +{ + u32 s = readl(reg); + + qtnf_non_posted_write(s & ~state, reg); +} + +static int qtnf_poll_state(__le32 __iomem *reg, u32 state, u32 delay_in_ms) +{ + u32 timeout = 0; + + while ((qtnf_is_state(reg, state) == 0)) { + usleep_range(1000, 1200); + if (++timeout > delay_in_ms) + return -1; + } + + return 0; +} + +static int pearl_alloc_bd_table(struct qtnf_pcie_pearl_state *ps) +{ + struct qtnf_pcie_bus_priv *priv = &ps->base; + dma_addr_t paddr; + void *vaddr; + int len; + + len = priv->tx_bd_num * sizeof(struct qtnf_pearl_tx_bd) + + priv->rx_bd_num * sizeof(struct qtnf_pearl_rx_bd); + + vaddr = dmam_alloc_coherent(&priv->pdev->dev, len, &paddr, GFP_KERNEL); + if (!vaddr) + return -ENOMEM; + + /* tx bd */ + + memset(vaddr, 0, len); + + ps->bd_table_vaddr = vaddr; + ps->bd_table_paddr = paddr; + ps->bd_table_len = len; + + ps->tx_bd_vbase = vaddr; + ps->tx_bd_pbase = paddr; + + pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); + + priv->tx_bd_r_index = 0; + priv->tx_bd_w_index = 0; + + /* rx bd */ + + vaddr = ((struct qtnf_pearl_tx_bd *)vaddr) + priv->tx_bd_num; + paddr += priv->tx_bd_num * sizeof(struct qtnf_pearl_tx_bd); + + ps->rx_bd_vbase = vaddr; + ps->rx_bd_pbase = paddr; + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + writel(QTN_HOST_HI32(paddr), + PCIE_HDP_TX_HOST_Q_BASE_H(ps->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(paddr), + PCIE_HDP_TX_HOST_Q_BASE_L(ps->pcie_reg_base)); + writel(priv->rx_bd_num | (sizeof(struct qtnf_pearl_rx_bd)) << 16, + PCIE_HDP_TX_HOST_Q_SZ_CTRL(ps->pcie_reg_base)); + + pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); + + return 0; +} + +static int pearl_skb2rbd_attach(struct qtnf_pcie_pearl_state *ps, u16 index) +{ + struct qtnf_pcie_bus_priv *priv = &ps->base; + struct qtnf_pearl_rx_bd *rxbd; + struct sk_buff *skb; + dma_addr_t paddr; + + skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC); + if (!skb) { + priv->rx_skb[index] = NULL; + return -ENOMEM; + } + + priv->rx_skb[index] = skb; + rxbd = &ps->rx_bd_vbase[index]; + + paddr = pci_map_single(priv->pdev, skb->data, + SKB_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(priv->pdev, paddr)) { + pr_err("skb DMA mapping error: %pad\n", &paddr); + return -ENOMEM; + } + + /* keep rx skb paddrs in rx buffer descriptors for cleanup purposes */ + rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr)); + rxbd->addr_h = cpu_to_le32(QTN_HOST_HI32(paddr)); + rxbd->info = 0x0; + + priv->rx_bd_w_index = index; + + /* sync up all descriptor updates */ + wmb(); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + writel(QTN_HOST_HI32(paddr), + PCIE_HDP_HHBM_BUF_PTR_H(ps->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(paddr), + PCIE_HDP_HHBM_BUF_PTR(ps->pcie_reg_base)); + + writel(index, PCIE_HDP_TX_HOST_Q_WR_PTR(ps->pcie_reg_base)); + return 0; +} + +static int pearl_alloc_rx_buffers(struct qtnf_pcie_pearl_state *ps) +{ + u16 i; + int ret = 0; + + memset(ps->rx_bd_vbase, 0x0, + ps->base.rx_bd_num * sizeof(struct qtnf_pearl_rx_bd)); + + for (i = 0; i < ps->base.rx_bd_num; i++) { + ret = pearl_skb2rbd_attach(ps, i); + if (ret) + break; + } + + return ret; +} + +/* all rx/tx activity should have ceased before calling this function */ +static void qtnf_pearl_free_xfer_buffers(struct qtnf_pcie_pearl_state *ps) +{ + struct qtnf_pcie_bus_priv *priv = &ps->base; + struct qtnf_pearl_tx_bd *txbd; + struct qtnf_pearl_rx_bd *rxbd; + struct sk_buff *skb; + dma_addr_t paddr; + int i; + + /* free rx buffers */ + for (i = 0; i < priv->rx_bd_num; i++) { + if (priv->rx_skb && priv->rx_skb[i]) { + rxbd = &ps->rx_bd_vbase[i]; + skb = priv->rx_skb[i]; + paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), + le32_to_cpu(rxbd->addr)); + pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + priv->rx_skb[i] = NULL; + } + } + + /* free tx buffers */ + for (i = 0; i < priv->tx_bd_num; i++) { + if (priv->tx_skb && priv->tx_skb[i]) { + txbd = &ps->tx_bd_vbase[i]; + skb = priv->tx_skb[i]; + paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), + le32_to_cpu(txbd->addr)); + pci_unmap_single(priv->pdev, paddr, skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb_any(skb); + priv->tx_skb[i] = NULL; + } + } +} + +static int pearl_hhbm_init(struct qtnf_pcie_pearl_state *ps) +{ + u32 val; + + val = readl(PCIE_HHBM_CONFIG(ps->pcie_reg_base)); + val |= HHBM_CONFIG_SOFT_RESET; + writel(val, PCIE_HHBM_CONFIG(ps->pcie_reg_base)); + usleep_range(50, 100); + val &= ~HHBM_CONFIG_SOFT_RESET; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + val |= HHBM_64BIT; +#endif + writel(val, PCIE_HHBM_CONFIG(ps->pcie_reg_base)); + writel(ps->base.rx_bd_num, PCIE_HHBM_Q_LIMIT_REG(ps->pcie_reg_base)); + + return 0; +} + +static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps) +{ + struct qtnf_pcie_bus_priv *priv = &ps->base; + int ret; + u32 val; + + priv->tx_bd_num = tx_bd_size_param; + priv->rx_bd_num = rx_bd_size_param; + priv->rx_bd_w_index = 0; + priv->rx_bd_r_index = 0; + + if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) { + pr_err("tx_bd_size_param %u is not power of two\n", + priv->tx_bd_num); + return -EINVAL; + } + + val = priv->tx_bd_num * sizeof(struct qtnf_pearl_tx_bd); + if (val > PCIE_HHBM_MAX_SIZE) { + pr_err("tx_bd_size_param %u is too large\n", + priv->tx_bd_num); + return -EINVAL; + } + + if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) { + pr_err("rx_bd_size_param %u is not power of two\n", + priv->rx_bd_num); + return -EINVAL; + } + + val = priv->rx_bd_num * sizeof(dma_addr_t); + if (val > PCIE_HHBM_MAX_SIZE) { + pr_err("rx_bd_size_param %u is too large\n", + priv->rx_bd_num); + return -EINVAL; + } + + ret = pearl_hhbm_init(ps); + if (ret) { + pr_err("failed to init h/w queues\n"); + return ret; + } + + ret = qtnf_pcie_alloc_skb_array(priv); + if (ret) { + pr_err("failed to allocate skb array\n"); + return ret; + } + + ret = pearl_alloc_bd_table(ps); + if (ret) { + pr_err("failed to allocate bd table\n"); + return ret; + } + + ret = pearl_alloc_rx_buffers(ps); + if (ret) { + pr_err("failed to allocate rx buffers\n"); + return ret; + } + + return ret; +} + +static void qtnf_pearl_data_tx_reclaim(struct qtnf_pcie_pearl_state *ps) +{ + struct qtnf_pcie_bus_priv *priv = &ps->base; + struct qtnf_pearl_tx_bd *txbd; + struct sk_buff *skb; + unsigned long flags; + dma_addr_t paddr; + u32 tx_done_index; + int count = 0; + int i; + + spin_lock_irqsave(&priv->tx_reclaim_lock, flags); + + tx_done_index = readl(PCIE_HDP_RX0DMA_CNT(ps->pcie_reg_base)) + & (priv->tx_bd_num - 1); + + i = priv->tx_bd_r_index; + + while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) { + skb = priv->tx_skb[i]; + if (likely(skb)) { + txbd = &ps->tx_bd_vbase[i]; + paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), + le32_to_cpu(txbd->addr)); + pci_unmap_single(priv->pdev, paddr, skb->len, + PCI_DMA_TODEVICE); + + if (skb->dev) { + qtnf_update_tx_stats(skb->dev, skb); + if (unlikely(priv->tx_stopped)) { + qtnf_wake_all_queues(skb->dev); + priv->tx_stopped = 0; + } + } + + dev_kfree_skb_any(skb); + } + + priv->tx_skb[i] = NULL; + count++; + + if (++i >= priv->tx_bd_num) + i = 0; + } + + priv->tx_reclaim_done += count; + priv->tx_reclaim_req++; + priv->tx_bd_r_index = i; + + spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags); +} + +static int qtnf_tx_queue_ready(struct qtnf_pcie_pearl_state *ps) +{ + struct qtnf_pcie_bus_priv *priv = &ps->base; + + if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)) { + qtnf_pearl_data_tx_reclaim(ps); + + if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)) { + pr_warn_ratelimited("reclaim full Tx queue\n"); + priv->tx_full_count++; + return 0; + } + } + + return 1; +} + +static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) +{ + struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus); + struct qtnf_pcie_bus_priv *priv = &ps->base; + dma_addr_t txbd_paddr, skb_paddr; + struct qtnf_pearl_tx_bd *txbd; + unsigned long flags; + int len, i; + u32 info; + int ret = 0; + + spin_lock_irqsave(&priv->tx_lock, flags); + + if (!qtnf_tx_queue_ready(ps)) { + if (skb->dev) { + netif_tx_stop_all_queues(skb->dev); + priv->tx_stopped = 1; + } + + spin_unlock_irqrestore(&priv->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + i = priv->tx_bd_w_index; + priv->tx_skb[i] = skb; + len = skb->len; + + skb_paddr = pci_map_single(priv->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, skb_paddr)) { + pr_err("skb DMA mapping error: %pad\n", &skb_paddr); + ret = -ENOMEM; + goto tx_done; + } + + txbd = &ps->tx_bd_vbase[i]; + txbd->addr = cpu_to_le32(QTN_HOST_LO32(skb_paddr)); + txbd->addr_h = cpu_to_le32(QTN_HOST_HI32(skb_paddr)); + + info = (len & QTN_PCIE_TX_DESC_LEN_MASK) << QTN_PCIE_TX_DESC_LEN_SHIFT; + txbd->info = cpu_to_le32(info); + + /* sync up all descriptor updates before passing them to EP */ + dma_wmb(); + + /* write new TX descriptor to PCIE_RX_FIFO on EP */ + txbd_paddr = ps->tx_bd_pbase + i * sizeof(struct qtnf_pearl_tx_bd); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + writel(QTN_HOST_HI32(txbd_paddr), + PCIE_HDP_HOST_WR_DESC0_H(ps->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(txbd_paddr), + PCIE_HDP_HOST_WR_DESC0(ps->pcie_reg_base)); + + if (++i >= priv->tx_bd_num) + i = 0; + + priv->tx_bd_w_index = i; + +tx_done: + if (ret && skb) { + pr_err_ratelimited("drop skb\n"); + if (skb->dev) + skb->dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + } + + priv->tx_done_count++; + spin_unlock_irqrestore(&priv->tx_lock, flags); + + qtnf_pearl_data_tx_reclaim(ps); + + return NETDEV_TX_OK; +} + +static irqreturn_t qtnf_pcie_pearl_interrupt(int irq, void *data) +{ + struct qtnf_bus *bus = (struct qtnf_bus *)data; + struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus); + struct qtnf_pcie_bus_priv *priv = &ps->base; + u32 status; + + priv->pcie_irq_count++; + status = readl(PCIE_HDP_INT_STATUS(ps->pcie_reg_base)); + + qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in); + qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_out); + + if (!(status & ps->pcie_irq_mask)) + goto irq_done; + + if (status & PCIE_HDP_INT_RX_BITS) + ps->pcie_irq_rx_count++; + + if (status & PCIE_HDP_INT_TX_BITS) + ps->pcie_irq_tx_count++; + + if (status & PCIE_HDP_INT_HHBM_UF) + ps->pcie_irq_uf_count++; + + if (status & PCIE_HDP_INT_RX_BITS) { + qtnf_dis_rxdone_irq(ps); + napi_schedule(&bus->mux_napi); + } + + if (status & PCIE_HDP_INT_TX_BITS) { + qtnf_dis_txdone_irq(ps); + tasklet_hi_schedule(&priv->reclaim_tq); + } + +irq_done: + /* H/W workaround: clean all bits, not only enabled */ + qtnf_non_posted_write(~0U, PCIE_HDP_INT_STATUS(ps->pcie_reg_base)); + + if (!priv->msi_enabled) + qtnf_deassert_intx(ps); + + return IRQ_HANDLED; +} + +static int qtnf_rx_data_ready(struct qtnf_pcie_pearl_state *ps) +{ + u16 index = ps->base.rx_bd_r_index; + struct qtnf_pearl_rx_bd *rxbd; + u32 descw; + + rxbd = &ps->rx_bd_vbase[index]; + descw = le32_to_cpu(rxbd->info); + + if (descw & QTN_TXDONE_MASK) + return 1; + + return 0; +} + +static int qtnf_pcie_pearl_rx_poll(struct napi_struct *napi, int budget) +{ + struct qtnf_bus *bus = container_of(napi, struct qtnf_bus, mux_napi); + struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus); + struct qtnf_pcie_bus_priv *priv = &ps->base; + struct net_device *ndev = NULL; + struct sk_buff *skb = NULL; + int processed = 0; + struct qtnf_pearl_rx_bd *rxbd; + dma_addr_t skb_paddr; + int consume; + u32 descw; + u32 psize; + u16 r_idx; + u16 w_idx; + int ret; + + while (processed < budget) { + if (!qtnf_rx_data_ready(ps)) + goto rx_out; + + r_idx = priv->rx_bd_r_index; + rxbd = &ps->rx_bd_vbase[r_idx]; + descw = le32_to_cpu(rxbd->info); + + skb = priv->rx_skb[r_idx]; + psize = QTN_GET_LEN(descw); + consume = 1; + + if (!(descw & QTN_TXDONE_MASK)) { + pr_warn("skip invalid rxbd[%d]\n", r_idx); + consume = 0; + } + + if (!skb) { + pr_warn("skip missing rx_skb[%d]\n", r_idx); + consume = 0; + } + + if (skb && (skb_tailroom(skb) < psize)) { + pr_err("skip packet with invalid length: %u > %u\n", + psize, skb_tailroom(skb)); + consume = 0; + } + + if (skb) { + skb_paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), + le32_to_cpu(rxbd->addr)); + pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE, + PCI_DMA_FROMDEVICE); + } + + if (consume) { + skb_put(skb, psize); + ndev = qtnf_classify_skb(bus, skb); + if (likely(ndev)) { + qtnf_update_rx_stats(ndev, skb); + skb->protocol = eth_type_trans(skb, ndev); + napi_gro_receive(napi, skb); + } else { + pr_debug("drop untagged skb\n"); + bus->mux_dev.stats.rx_dropped++; + dev_kfree_skb_any(skb); + } + } else { + if (skb) { + bus->mux_dev.stats.rx_dropped++; + dev_kfree_skb_any(skb); + } + } + + priv->rx_skb[r_idx] = NULL; + if (++r_idx >= priv->rx_bd_num) + r_idx = 0; + + priv->rx_bd_r_index = r_idx; + + /* repalce processed buffer by a new one */ + w_idx = priv->rx_bd_w_index; + while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, + priv->rx_bd_num) > 0) { + if (++w_idx >= priv->rx_bd_num) + w_idx = 0; + + ret = pearl_skb2rbd_attach(ps, w_idx); + if (ret) { + pr_err("failed to allocate new rx_skb[%d]\n", + w_idx); + break; + } + } + + processed++; + } + +rx_out: + if (processed < budget) { + napi_complete(napi); + qtnf_en_rxdone_irq(ps); + } + + return processed; +} + +static void +qtnf_pcie_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev) +{ + struct qtnf_pcie_pearl_state *ps = (void *)get_bus_priv(bus); + + tasklet_hi_schedule(&ps->base.reclaim_tq); +} + +static void qtnf_pcie_data_rx_start(struct qtnf_bus *bus) +{ + struct qtnf_pcie_pearl_state *ps = (void *)get_bus_priv(bus); + + qtnf_enable_hdp_irqs(ps); + napi_enable(&bus->mux_napi); +} + +static void qtnf_pcie_data_rx_stop(struct qtnf_bus *bus) +{ + struct qtnf_pcie_pearl_state *ps = (void *)get_bus_priv(bus); + + napi_disable(&bus->mux_napi); + qtnf_disable_hdp_irqs(ps); +} + +static const struct qtnf_bus_ops qtnf_pcie_pearl_bus_ops = { + /* control path methods */ + .control_tx = qtnf_pcie_control_tx, + + /* data path methods */ + .data_tx = qtnf_pcie_data_tx, + .data_tx_timeout = qtnf_pcie_data_tx_timeout, + .data_rx_start = qtnf_pcie_data_rx_start, + .data_rx_stop = qtnf_pcie_data_rx_stop, +}; + +static int qtnf_dbg_irq_stats(struct seq_file *s, void *data) +{ + struct qtnf_bus *bus = dev_get_drvdata(s->private); + struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus); + u32 reg = readl(PCIE_HDP_INT_EN(ps->pcie_reg_base)); + u32 status; + + seq_printf(s, "pcie_irq_count(%u)\n", ps->base.pcie_irq_count); + seq_printf(s, "pcie_irq_tx_count(%u)\n", ps->pcie_irq_tx_count); + status = reg & PCIE_HDP_INT_TX_BITS; + seq_printf(s, "pcie_irq_tx_status(%s)\n", + (status == PCIE_HDP_INT_TX_BITS) ? "EN" : "DIS"); + seq_printf(s, "pcie_irq_rx_count(%u)\n", ps->pcie_irq_rx_count); + status = reg & PCIE_HDP_INT_RX_BITS; + seq_printf(s, "pcie_irq_rx_status(%s)\n", + (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS"); + seq_printf(s, "pcie_irq_uf_count(%u)\n", ps->pcie_irq_uf_count); + status = reg & PCIE_HDP_INT_HHBM_UF; + seq_printf(s, "pcie_irq_hhbm_uf_status(%s)\n", + (status == PCIE_HDP_INT_HHBM_UF) ? "EN" : "DIS"); + + return 0; +} + +static int qtnf_dbg_hdp_stats(struct seq_file *s, void *data) +{ + struct qtnf_bus *bus = dev_get_drvdata(s->private); + struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus); + struct qtnf_pcie_bus_priv *priv = &ps->base; + + seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count); + seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count); + seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done); + seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req); + + seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index); + seq_printf(s, "tx_bd_p_index(%u)\n", + readl(PCIE_HDP_RX0DMA_CNT(ps->pcie_reg_base)) + & (priv->tx_bd_num - 1)); + seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index); + seq_printf(s, "tx queue len(%u)\n", + CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)); + + seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index); + seq_printf(s, "rx_bd_p_index(%u)\n", + readl(PCIE_HDP_TX0DMA_CNT(ps->pcie_reg_base)) + & (priv->rx_bd_num - 1)); + seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index); + seq_printf(s, "rx alloc queue len(%u)\n", + CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, + priv->rx_bd_num)); + + return 0; +} + +static int qtnf_ep_fw_send(struct pci_dev *pdev, uint32_t size, + int blk, const u8 *pblk, const u8 *fw) +{ + struct qtnf_bus *bus = pci_get_drvdata(pdev); + + struct qtnf_pearl_fw_hdr *hdr; + u8 *pdata; + + int hds = sizeof(*hdr); + struct sk_buff *skb = NULL; + int len = 0; + int ret; + + skb = __dev_alloc_skb(QTN_PCIE_FW_BUFSZ, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb->len = QTN_PCIE_FW_BUFSZ; + skb->dev = NULL; + + hdr = (struct qtnf_pearl_fw_hdr *)skb->data; + memcpy(hdr->boardflg, QTN_PCIE_BOARDFLG, strlen(QTN_PCIE_BOARDFLG)); + hdr->fwsize = cpu_to_le32(size); + hdr->seqnum = cpu_to_le32(blk); + + if (blk) + hdr->type = cpu_to_le32(QTN_FW_DSUB); + else + hdr->type = cpu_to_le32(QTN_FW_DBEGIN); + + pdata = skb->data + hds; + + len = QTN_PCIE_FW_BUFSZ - hds; + if (pblk >= (fw + size - len)) { + len = fw + size - pblk; + hdr->type = cpu_to_le32(QTN_FW_DEND); + } + + hdr->pktlen = cpu_to_le32(len); + memcpy(pdata, pblk, len); + hdr->crc = cpu_to_le32(~crc32(0, pdata, len)); + + ret = qtnf_pcie_data_tx(bus, skb); + + return (ret == NETDEV_TX_OK) ? len : 0; +} + +static int +qtnf_ep_fw_load(struct qtnf_pcie_pearl_state *ps, const u8 *fw, u32 fw_size) +{ + int blk_size = QTN_PCIE_FW_BUFSZ - sizeof(struct qtnf_pearl_fw_hdr); + int blk_count = fw_size / blk_size + ((fw_size % blk_size) ? 1 : 0); + const u8 *pblk = fw; + int threshold = 0; + int blk = 0; + int len; + + pr_debug("FW upload started: fw_addr=0x%p size=%d\n", fw, fw_size); + + while (blk < blk_count) { + if (++threshold > 10000) { + pr_err("FW upload failed: too many retries\n"); + return -ETIMEDOUT; + } + + len = qtnf_ep_fw_send(ps->base.pdev, fw_size, blk, pblk, fw); + if (len <= 0) + continue; + + if (!((blk + 1) & QTN_PCIE_FW_DLMASK) || + (blk == (blk_count - 1))) { + qtnf_set_state(&ps->bda->bda_rc_state, + QTN_RC_FW_SYNC); + if (qtnf_poll_state(&ps->bda->bda_ep_state, + QTN_EP_FW_SYNC, + QTN_FW_DL_TIMEOUT_MS)) { + pr_err("FW upload failed: SYNC timed out\n"); + return -ETIMEDOUT; + } + + qtnf_clear_state(&ps->bda->bda_ep_state, + QTN_EP_FW_SYNC); + + if (qtnf_is_state(&ps->bda->bda_ep_state, + QTN_EP_FW_RETRY)) { + if (blk == (blk_count - 1)) { + int last_round = + blk_count & QTN_PCIE_FW_DLMASK; + blk -= last_round; + pblk -= ((last_round - 1) * + blk_size + len); + } else { + blk -= QTN_PCIE_FW_DLMASK; + pblk -= QTN_PCIE_FW_DLMASK * blk_size; + } + + qtnf_clear_state(&ps->bda->bda_ep_state, + QTN_EP_FW_RETRY); + + pr_warn("FW upload retry: block #%d\n", blk); + continue; + } + + qtnf_pearl_data_tx_reclaim(ps); + } + + pblk += len; + blk++; + } + + pr_debug("FW upload completed: totally sent %d blocks\n", blk); + return 0; +} + +static void qtnf_pearl_fw_work_handler(struct work_struct *work) +{ + struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work); + struct qtnf_pcie_pearl_state *ps = (void *)get_bus_priv(bus); + struct pci_dev *pdev = ps->base.pdev; + const struct firmware *fw; + int ret; + u32 state = QTN_RC_FW_LOADRDY | QTN_RC_FW_QLINK; + const char *fwname = QTN_PCI_PEARL_FW_NAME; + bool fw_boot_success = false; + + if (flashboot) { + state |= QTN_RC_FW_FLASHBOOT; + } else { + ret = request_firmware(&fw, fwname, &pdev->dev); + if (ret < 0) { + pr_err("failed to get firmware %s\n", fwname); + goto fw_load_exit; + } + } + + qtnf_set_state(&ps->bda->bda_rc_state, state); + + if (qtnf_poll_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY, + QTN_FW_DL_TIMEOUT_MS)) { + pr_err("card is not ready\n"); + + if (!flashboot) + release_firmware(fw); + + goto fw_load_exit; + } + + qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY); + + if (flashboot) { + pr_info("booting firmware from flash\n"); + + } else { + pr_info("starting firmware upload: %s\n", fwname); + + ret = qtnf_ep_fw_load(ps, fw->data, fw->size); + release_firmware(fw); + if (ret) { + pr_err("firmware upload error\n"); + goto fw_load_exit; + } + } + + if (qtnf_poll_state(&ps->bda->bda_ep_state, QTN_EP_FW_DONE, + QTN_FW_DL_TIMEOUT_MS)) { + pr_err("firmware bringup timed out\n"); + goto fw_load_exit; + } + + pr_info("firmware is up and running\n"); + + if (qtnf_poll_state(&ps->bda->bda_ep_state, + QTN_EP_FW_QLINK_DONE, QTN_FW_QLINK_TIMEOUT_MS)) { + pr_err("firmware runtime failure\n"); + goto fw_load_exit; + } + + fw_boot_success = true; + +fw_load_exit: + qtnf_pcie_fw_boot_done(bus, fw_boot_success, DRV_NAME); + + if (fw_boot_success) { + qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats); + qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats); + } +} + +static void qtnf_pearl_reclaim_tasklet_fn(unsigned long data) +{ + struct qtnf_pcie_pearl_state *ps = (void *)data; + + qtnf_pearl_data_tx_reclaim(ps); + qtnf_en_txdone_irq(ps); +} + +static int qtnf_pearl_check_chip_id(struct qtnf_pcie_pearl_state *ps) +{ + unsigned int chipid; + + chipid = qtnf_chip_id_get(ps->base.sysctl_bar); + + switch (chipid) { + case QTN_CHIP_ID_PEARL: + case QTN_CHIP_ID_PEARL_B: + case QTN_CHIP_ID_PEARL_C: + pr_info("chip ID is 0x%x\n", chipid); + break; + default: + pr_err("incorrect chip ID 0x%x\n", chipid); + return -ENODEV; + } + + return 0; +} + +static int qtnf_pcie_pearl_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct qtnf_shm_ipc_int ipc_int; + struct qtnf_pcie_pearl_state *ps; + struct qtnf_bus *bus; + int ret; + u64 dma_mask; + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dma_mask = DMA_BIT_MASK(64); +#else + dma_mask = DMA_BIT_MASK(32); +#endif + + ret = qtnf_pcie_probe(pdev, sizeof(*ps), &qtnf_pcie_pearl_bus_ops, + dma_mask, use_msi); + if (ret) + return ret; + + bus = pci_get_drvdata(pdev); + ps = get_bus_priv(bus); + + spin_lock_init(&ps->irq_lock); + + tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn, + (unsigned long)ps); + netif_napi_add(&bus->mux_dev, &bus->mux_napi, + qtnf_pcie_pearl_rx_poll, 10); + INIT_WORK(&bus->fw_work, qtnf_pearl_fw_work_handler); + + ps->pcie_reg_base = ps->base.dmareg_bar; + ps->bda = ps->base.epmem_bar; + writel(ps->base.msi_enabled, &ps->bda->bda_rc_msi_enabled); + + ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int; + ipc_int.arg = ps; + qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1, + &ps->bda->bda_shm_reg2, &ipc_int); + + ret = qtnf_pearl_check_chip_id(ps); + if (ret) + goto error; + + ret = qtnf_pcie_pearl_init_xfer(ps); + if (ret) { + pr_err("PCIE xfer init failed\n"); + goto error; + } + + /* init default irq settings */ + qtnf_init_hdp_irqs(ps); + + /* start with disabled irqs */ + qtnf_disable_hdp_irqs(ps); + + ret = devm_request_irq(&pdev->dev, pdev->irq, + &qtnf_pcie_pearl_interrupt, 0, + "qtnf_pcie_irq", (void *)bus); + if (ret) { + pr_err("failed to request pcie irq %d\n", pdev->irq); + goto err_xfer; + } + + qtnf_pcie_bringup_fw_async(bus); + + return 0; + +err_xfer: + qtnf_pearl_free_xfer_buffers(ps); +error: + qtnf_pcie_remove(bus, &ps->base); + + return ret; +} + +static void qtnf_pcie_pearl_remove(struct pci_dev *pdev) +{ + struct qtnf_pcie_pearl_state *ps; + struct qtnf_bus *bus; + + bus = pci_get_drvdata(pdev); + if (!bus) + return; + + ps = get_bus_priv(bus); + + qtnf_pcie_remove(bus, &ps->base); + qtnf_pearl_reset_ep(ps); + qtnf_pearl_free_xfer_buffers(ps); +} + +#ifdef CONFIG_PM_SLEEP +static int qtnf_pcie_pearl_suspend(struct device *dev) +{ + return -EOPNOTSUPP; +} + +static int qtnf_pcie_pearl_resume(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_SLEEP +/* Power Management Hooks */ +static SIMPLE_DEV_PM_OPS(qtnf_pcie_pearl_pm_ops, qtnf_pcie_pearl_suspend, + qtnf_pcie_pearl_resume); +#endif + +static const struct pci_device_id qtnf_pcie_devid_table[] = { + { + PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table); + +static struct pci_driver qtnf_pcie_pearl_drv_data = { + .name = DRV_NAME, + .id_table = qtnf_pcie_devid_table, + .probe = qtnf_pcie_pearl_probe, + .remove = qtnf_pcie_pearl_remove, +#ifdef CONFIG_PM_SLEEP + .driver = { + .pm = &qtnf_pcie_pearl_pm_ops, + }, +#endif +}; + +static int __init qtnf_pcie_pearl_register(void) +{ + pr_info("register Quantenna QSR10g FullMAC PCIE driver\n"); + return pci_register_driver(&qtnf_pcie_pearl_drv_data); +} + +static void __exit qtnf_pcie_pearl_exit(void) +{ + pr_info("unregister Quantenna QSR10g FullMAC PCIE driver\n"); + pci_unregister_driver(&qtnf_pcie_pearl_drv_data); +} + +module_init(qtnf_pcie_pearl_register); +module_exit(qtnf_pcie_pearl_exit); + +MODULE_AUTHOR("Quantenna Communications"); +MODULE_DESCRIPTION("Quantenna QSR10g PCIe bus driver for 802.11 wireless LAN."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h index 00bb21a1c47a..f21e97ede090 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_ipc.h @@ -43,11 +43,6 @@ #define QTN_RC_FW_LOADRDY BIT(8) #define QTN_RC_FW_SYNC BIT(9) -/* state transition timeouts */ -#define QTN_FW_DL_TIMEOUT_MS 3000 -#define QTN_FW_QLINK_TIMEOUT_MS 30000 -#define QTN_EP_RESET_WAIT_MS 1000 - #define PCIE_HDP_INT_RX_BITS (0 \ | PCIE_HDP_INT_EP_TXDMA \ | PCIE_HDP_INT_EP_TXEMPTY \ @@ -68,17 +63,11 @@ #define QTN_HOST_ADDR(h, l) ((u32)l) #endif -#define QTN_SYSCTL_BAR 0 -#define QTN_SHMEM_BAR 2 -#define QTN_DMA_BAR 3 - #define QTN_PCIE_BDA_VERSION 0x1002 #define PCIE_BDA_NAMELEN 32 #define PCIE_HHBM_MAX_SIZE 2048 -#define SKB_BUF_SIZE 2048 - #define QTN_PCIE_BOARDFLG "PCIEQTN" #define QTN_PCIE_FW_DLMASK 0xF #define QTN_PCIE_FW_BUFSZ 2048 @@ -101,44 +90,6 @@ enum qtnf_pcie_bda_ipc_flags { QTN_PCIE_IPC_FLAG_SHM_PIO = BIT(1), }; -struct qtnf_pcie_bda { - __le16 bda_len; - __le16 bda_version; - __le32 bda_pci_endian; - __le32 bda_ep_state; - __le32 bda_rc_state; - __le32 bda_dma_mask; - __le32 bda_msi_addr; - __le32 bda_flashsz; - u8 bda_boardname[PCIE_BDA_NAMELEN]; - __le32 bda_rc_msi_enabled; - u8 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; - __le32 bda_dsbw_start_index; - __le32 bda_dsbw_end_index; - __le32 bda_dsbw_total_bytes; - __le32 bda_rc_tx_bd_base; - __le32 bda_rc_tx_bd_num; - u8 bda_pcie_mac[QTN_ENET_ADDR_LENGTH]; - struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096); /* host TX */ - struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096); /* host RX */ -} __packed; - -struct qtnf_tx_bd { - __le32 addr; - __le32 addr_h; - __le32 info; - __le32 info_h; -} __packed; - -struct qtnf_rx_bd { - __le32 addr; - __le32 addr_h; - __le32 info; - __le32 info_h; - __le32 next_ptr; - __le32 next_ptr_h; -} __packed; - enum qtnf_fw_loadtype { QTN_FW_DBEGIN, QTN_FW_DSUB, @@ -146,13 +97,4 @@ enum qtnf_fw_loadtype { QTN_FW_CTRL }; -struct qtnf_pcie_fw_hdr { - u8 boardflg[8]; - __le32 fwsize; - __le32 seqnum; - __le32 type; - __le32 pktlen; - __le32 crc; -} __packed; - #endif /* _QTN_FMAC_PCIE_IPC_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h index 0bfe285b6b48..0bfe285b6b48 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie_regs.h diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c deleted file mode 100644 index 3120d49df565..000000000000 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ /dev/null @@ -1,1494 +0,0 @@ -/* - * Copyright (c) 2015-2016 Quantenna Communications, Inc. - * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * 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. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/firmware.h> -#include <linux/pci.h> -#include <linux/vmalloc.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/sched.h> -#include <linux/completion.h> -#include <linux/crc32.h> -#include <linux/spinlock.h> -#include <linux/circ_buf.h> -#include <linux/log2.h> - -#include "qtn_hw_ids.h" -#include "pcie_bus_priv.h" -#include "core.h" -#include "bus.h" -#include "debug.h" - -static bool use_msi = true; -module_param(use_msi, bool, 0644); -MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt"); - -static unsigned int tx_bd_size_param = 32; -module_param(tx_bd_size_param, uint, 0644); -MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two"); - -static unsigned int rx_bd_size_param = 256; -module_param(rx_bd_size_param, uint, 0644); -MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two"); - -static u8 flashboot = 1; -module_param(flashboot, byte, 0644); -MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS"); - -#define DRV_NAME "qtnfmac_pearl_pcie" - -static inline void qtnf_non_posted_write(u32 val, void __iomem *basereg) -{ - writel(val, basereg); - - /* flush posted write */ - readl(basereg); -} - -static inline void qtnf_init_hdp_irqs(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - priv->pcie_irq_mask = (PCIE_HDP_INT_RX_BITS | PCIE_HDP_INT_TX_BITS); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void qtnf_enable_hdp_irqs(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - writel(priv->pcie_irq_mask, PCIE_HDP_INT_EN(priv->pcie_reg_base)); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void qtnf_disable_hdp_irqs(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - writel(0x0, PCIE_HDP_INT_EN(priv->pcie_reg_base)); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void qtnf_en_rxdone_irq(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - priv->pcie_irq_mask |= PCIE_HDP_INT_RX_BITS; - writel(priv->pcie_irq_mask, PCIE_HDP_INT_EN(priv->pcie_reg_base)); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void qtnf_dis_rxdone_irq(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - priv->pcie_irq_mask &= ~PCIE_HDP_INT_RX_BITS; - writel(priv->pcie_irq_mask, PCIE_HDP_INT_EN(priv->pcie_reg_base)); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void qtnf_en_txdone_irq(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - priv->pcie_irq_mask |= PCIE_HDP_INT_TX_BITS; - writel(priv->pcie_irq_mask, PCIE_HDP_INT_EN(priv->pcie_reg_base)); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void qtnf_dis_txdone_irq(struct qtnf_pcie_bus_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - priv->pcie_irq_mask &= ~PCIE_HDP_INT_TX_BITS; - writel(priv->pcie_irq_mask, PCIE_HDP_INT_EN(priv->pcie_reg_base)); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv) -{ - struct pci_dev *pdev = priv->pdev; - - /* fall back to legacy INTx interrupts by default */ - priv->msi_enabled = 0; - - /* check if MSI capability is available */ - if (use_msi) { - if (!pci_enable_msi(pdev)) { - pr_debug("MSI interrupt enabled\n"); - priv->msi_enabled = 1; - } else { - pr_warn("failed to enable MSI interrupts"); - } - } - - if (!priv->msi_enabled) { - pr_warn("legacy PCIE interrupts enabled\n"); - pci_intx(pdev, 1); - } -} - -static void qtnf_deassert_intx(struct qtnf_pcie_bus_priv *priv) -{ - void __iomem *reg = priv->sysctl_bar + PEARL_PCIE_CFG0_OFFSET; - u32 cfg; - - cfg = readl(reg); - cfg &= ~PEARL_ASSERT_INTX; - qtnf_non_posted_write(cfg, reg); -} - -static void qtnf_reset_card(struct qtnf_pcie_bus_priv *priv) -{ - const u32 data = QTN_PEARL_IPC_IRQ_WORD(QTN_PEARL_LHOST_EP_RESET); - void __iomem *reg = priv->sysctl_bar + - QTN_PEARL_SYSCTL_LHOST_IRQ_OFFSET; - - qtnf_non_posted_write(data, reg); - msleep(QTN_EP_RESET_WAIT_MS); - pci_restore_state(priv->pdev); -} - -static void qtnf_ipc_gen_ep_int(void *arg) -{ - const struct qtnf_pcie_bus_priv *priv = arg; - const u32 data = QTN_PEARL_IPC_IRQ_WORD(QTN_PEARL_LHOST_IPC_IRQ); - void __iomem *reg = priv->sysctl_bar + - QTN_PEARL_SYSCTL_LHOST_IRQ_OFFSET; - - qtnf_non_posted_write(data, reg); -} - -static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index) -{ - void __iomem *vaddr; - dma_addr_t busaddr; - size_t len; - int ret; - - ret = pcim_iomap_regions(priv->pdev, 1 << index, DRV_NAME); - if (ret) - return IOMEM_ERR_PTR(ret); - - busaddr = pci_resource_start(priv->pdev, index); - len = pci_resource_len(priv->pdev, index); - vaddr = pcim_iomap_table(priv->pdev)[index]; - if (!vaddr) - return IOMEM_ERR_PTR(-ENOMEM); - - pr_debug("BAR%u vaddr=0x%p busaddr=%pad len=%u\n", - index, vaddr, &busaddr, (int)len); - - return vaddr; -} - -static void qtnf_pcie_control_rx_callback(void *arg, const u8 *buf, size_t len) -{ - struct qtnf_pcie_bus_priv *priv = arg; - struct qtnf_bus *bus = pci_get_drvdata(priv->pdev); - struct sk_buff *skb; - - if (unlikely(len == 0)) { - pr_warn("zero length packet received\n"); - return; - } - - skb = __dev_alloc_skb(len, GFP_KERNEL); - - if (unlikely(!skb)) { - pr_err("failed to allocate skb\n"); - return; - } - - skb_put_data(skb, buf, len); - - qtnf_trans_handle_rx_ctl_packet(bus, skb); -} - -static int qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv) -{ - struct qtnf_shm_ipc_region __iomem *ipc_tx_reg; - struct qtnf_shm_ipc_region __iomem *ipc_rx_reg; - const struct qtnf_shm_ipc_int ipc_int = { qtnf_ipc_gen_ep_int, priv }; - const struct qtnf_shm_ipc_rx_callback rx_callback = { - qtnf_pcie_control_rx_callback, priv }; - - ipc_tx_reg = &priv->bda->bda_shm_reg1; - ipc_rx_reg = &priv->bda->bda_shm_reg2; - - qtnf_shm_ipc_init(&priv->shm_ipc_ep_in, QTNF_SHM_IPC_OUTBOUND, - ipc_tx_reg, priv->workqueue, - &ipc_int, &rx_callback); - qtnf_shm_ipc_init(&priv->shm_ipc_ep_out, QTNF_SHM_IPC_INBOUND, - ipc_rx_reg, priv->workqueue, - &ipc_int, &rx_callback); - - return 0; -} - -static void qtnf_pcie_free_shm_ipc(struct qtnf_pcie_bus_priv *priv) -{ - qtnf_shm_ipc_free(&priv->shm_ipc_ep_in); - qtnf_shm_ipc_free(&priv->shm_ipc_ep_out); -} - -static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv) -{ - int ret = -ENOMEM; - - priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR); - if (IS_ERR(priv->sysctl_bar)) { - pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR); - return ret; - } - - priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR); - if (IS_ERR(priv->dmareg_bar)) { - pr_err("failed to map BAR%u\n", QTN_DMA_BAR); - return ret; - } - - priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR); - if (IS_ERR(priv->epmem_bar)) { - pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR); - return ret; - } - - priv->pcie_reg_base = priv->dmareg_bar; - priv->bda = priv->epmem_bar; - writel(priv->msi_enabled, &priv->bda->bda_rc_msi_enabled); - - return 0; -} - -static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv) -{ - struct pci_dev *pdev = priv->pdev; - struct pci_dev *parent; - int mps_p, mps_o, mps_m, mps; - int ret; - - /* current mps */ - mps_o = pcie_get_mps(pdev); - - /* maximum supported mps */ - mps_m = 128 << pdev->pcie_mpss; - - /* suggested new mps value */ - mps = mps_m; - - if (pdev->bus && pdev->bus->self) { - /* parent (bus) mps */ - parent = pdev->bus->self; - - if (pci_is_pcie(parent)) { - mps_p = pcie_get_mps(parent); - mps = min(mps_m, mps_p); - } - } - - ret = pcie_set_mps(pdev, mps); - if (ret) { - pr_err("failed to set mps to %d, keep using current %d\n", - mps, mps_o); - priv->mps = mps_o; - return; - } - - pr_debug("set mps to %d (was %d, max %d)\n", mps, mps_o, mps_m); - priv->mps = mps; -} - -static int qtnf_is_state(__le32 __iomem *reg, u32 state) -{ - u32 s = readl(reg); - - return s & state; -} - -static void qtnf_set_state(__le32 __iomem *reg, u32 state) -{ - u32 s = readl(reg); - - qtnf_non_posted_write(state | s, reg); -} - -static void qtnf_clear_state(__le32 __iomem *reg, u32 state) -{ - u32 s = readl(reg); - - qtnf_non_posted_write(s & ~state, reg); -} - -static int qtnf_poll_state(__le32 __iomem *reg, u32 state, u32 delay_in_ms) -{ - u32 timeout = 0; - - while ((qtnf_is_state(reg, state) == 0)) { - usleep_range(1000, 1200); - if (++timeout > delay_in_ms) - return -1; - } - - return 0; -} - -static int alloc_skb_array(struct qtnf_pcie_bus_priv *priv) -{ - struct sk_buff **vaddr; - int len; - - len = priv->tx_bd_num * sizeof(*priv->tx_skb) + - priv->rx_bd_num * sizeof(*priv->rx_skb); - vaddr = devm_kzalloc(&priv->pdev->dev, len, GFP_KERNEL); - - if (!vaddr) - return -ENOMEM; - - priv->tx_skb = vaddr; - - vaddr += priv->tx_bd_num; - priv->rx_skb = vaddr; - - return 0; -} - -static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) -{ - dma_addr_t paddr; - void *vaddr; - int len; - - len = priv->tx_bd_num * sizeof(struct qtnf_tx_bd) + - priv->rx_bd_num * sizeof(struct qtnf_rx_bd); - - vaddr = dmam_alloc_coherent(&priv->pdev->dev, len, &paddr, GFP_KERNEL); - if (!vaddr) - return -ENOMEM; - - /* tx bd */ - - memset(vaddr, 0, len); - - priv->bd_table_vaddr = vaddr; - priv->bd_table_paddr = paddr; - priv->bd_table_len = len; - - priv->tx_bd_vbase = vaddr; - priv->tx_bd_pbase = paddr; - - pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); - - priv->tx_bd_r_index = 0; - priv->tx_bd_w_index = 0; - - /* rx bd */ - - vaddr = ((struct qtnf_tx_bd *)vaddr) + priv->tx_bd_num; - paddr += priv->tx_bd_num * sizeof(struct qtnf_tx_bd); - - priv->rx_bd_vbase = vaddr; - priv->rx_bd_pbase = paddr; - -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - writel(QTN_HOST_HI32(paddr), - PCIE_HDP_TX_HOST_Q_BASE_H(priv->pcie_reg_base)); -#endif - writel(QTN_HOST_LO32(paddr), - PCIE_HDP_TX_HOST_Q_BASE_L(priv->pcie_reg_base)); - writel(priv->rx_bd_num | (sizeof(struct qtnf_rx_bd)) << 16, - PCIE_HDP_TX_HOST_Q_SZ_CTRL(priv->pcie_reg_base)); - - pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); - - return 0; -} - -static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 index) -{ - struct qtnf_rx_bd *rxbd; - struct sk_buff *skb; - dma_addr_t paddr; - - skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC); - if (!skb) { - priv->rx_skb[index] = NULL; - return -ENOMEM; - } - - priv->rx_skb[index] = skb; - rxbd = &priv->rx_bd_vbase[index]; - - paddr = pci_map_single(priv->pdev, skb->data, - SKB_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(priv->pdev, paddr)) { - pr_err("skb DMA mapping error: %pad\n", &paddr); - return -ENOMEM; - } - - /* keep rx skb paddrs in rx buffer descriptors for cleanup purposes */ - rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr)); - rxbd->addr_h = cpu_to_le32(QTN_HOST_HI32(paddr)); - rxbd->info = 0x0; - - priv->rx_bd_w_index = index; - - /* sync up all descriptor updates */ - wmb(); - -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - writel(QTN_HOST_HI32(paddr), - PCIE_HDP_HHBM_BUF_PTR_H(priv->pcie_reg_base)); -#endif - writel(QTN_HOST_LO32(paddr), - PCIE_HDP_HHBM_BUF_PTR(priv->pcie_reg_base)); - - writel(index, PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); - return 0; -} - -static int alloc_rx_buffers(struct qtnf_pcie_bus_priv *priv) -{ - u16 i; - int ret = 0; - - memset(priv->rx_bd_vbase, 0x0, - priv->rx_bd_num * sizeof(struct qtnf_rx_bd)); - - for (i = 0; i < priv->rx_bd_num; i++) { - ret = skb2rbd_attach(priv, i); - if (ret) - break; - } - - return ret; -} - -/* all rx/tx activity should have ceased before calling this function */ -static void qtnf_free_xfer_buffers(struct qtnf_pcie_bus_priv *priv) -{ - struct qtnf_tx_bd *txbd; - struct qtnf_rx_bd *rxbd; - struct sk_buff *skb; - dma_addr_t paddr; - int i; - - /* free rx buffers */ - for (i = 0; i < priv->rx_bd_num; i++) { - if (priv->rx_skb && priv->rx_skb[i]) { - rxbd = &priv->rx_bd_vbase[i]; - skb = priv->rx_skb[i]; - paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), - le32_to_cpu(rxbd->addr)); - pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - priv->rx_skb[i] = NULL; - } - } - - /* free tx buffers */ - for (i = 0; i < priv->tx_bd_num; i++) { - if (priv->tx_skb && priv->tx_skb[i]) { - txbd = &priv->tx_bd_vbase[i]; - skb = priv->tx_skb[i]; - paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), - le32_to_cpu(txbd->addr)); - pci_unmap_single(priv->pdev, paddr, skb->len, - PCI_DMA_TODEVICE); - dev_kfree_skb_any(skb); - priv->tx_skb[i] = NULL; - } - } -} - -static int qtnf_hhbm_init(struct qtnf_pcie_bus_priv *priv) -{ - u32 val; - - val = readl(PCIE_HHBM_CONFIG(priv->pcie_reg_base)); - val |= HHBM_CONFIG_SOFT_RESET; - writel(val, PCIE_HHBM_CONFIG(priv->pcie_reg_base)); - usleep_range(50, 100); - val &= ~HHBM_CONFIG_SOFT_RESET; -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - val |= HHBM_64BIT; -#endif - writel(val, PCIE_HHBM_CONFIG(priv->pcie_reg_base)); - writel(priv->rx_bd_num, PCIE_HHBM_Q_LIMIT_REG(priv->pcie_reg_base)); - - return 0; -} - -static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) -{ - int ret; - u32 val; - - priv->tx_bd_num = tx_bd_size_param; - priv->rx_bd_num = rx_bd_size_param; - priv->rx_bd_w_index = 0; - priv->rx_bd_r_index = 0; - - if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) { - pr_err("tx_bd_size_param %u is not power of two\n", - priv->tx_bd_num); - return -EINVAL; - } - - val = priv->tx_bd_num * sizeof(struct qtnf_tx_bd); - if (val > PCIE_HHBM_MAX_SIZE) { - pr_err("tx_bd_size_param %u is too large\n", - priv->tx_bd_num); - return -EINVAL; - } - - if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) { - pr_err("rx_bd_size_param %u is not power of two\n", - priv->rx_bd_num); - return -EINVAL; - } - - val = priv->rx_bd_num * sizeof(dma_addr_t); - if (val > PCIE_HHBM_MAX_SIZE) { - pr_err("rx_bd_size_param %u is too large\n", - priv->rx_bd_num); - return -EINVAL; - } - - ret = qtnf_hhbm_init(priv); - if (ret) { - pr_err("failed to init h/w queues\n"); - return ret; - } - - ret = alloc_skb_array(priv); - if (ret) { - pr_err("failed to allocate skb array\n"); - return ret; - } - - ret = alloc_bd_table(priv); - if (ret) { - pr_err("failed to allocate bd table\n"); - return ret; - } - - ret = alloc_rx_buffers(priv); - if (ret) { - pr_err("failed to allocate rx buffers\n"); - return ret; - } - - return ret; -} - -static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) -{ - struct qtnf_tx_bd *txbd; - struct sk_buff *skb; - unsigned long flags; - dma_addr_t paddr; - u32 tx_done_index; - int count = 0; - int i; - - spin_lock_irqsave(&priv->tx_reclaim_lock, flags); - - tx_done_index = readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) - & (priv->tx_bd_num - 1); - - i = priv->tx_bd_r_index; - - while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) { - skb = priv->tx_skb[i]; - if (likely(skb)) { - txbd = &priv->tx_bd_vbase[i]; - paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), - le32_to_cpu(txbd->addr)); - pci_unmap_single(priv->pdev, paddr, skb->len, - PCI_DMA_TODEVICE); - - if (skb->dev) { - qtnf_update_tx_stats(skb->dev, skb); - if (unlikely(priv->tx_stopped)) { - qtnf_wake_all_queues(skb->dev); - priv->tx_stopped = 0; - } - } - - dev_kfree_skb_any(skb); - } - - priv->tx_skb[i] = NULL; - count++; - - if (++i >= priv->tx_bd_num) - i = 0; - } - - priv->tx_reclaim_done += count; - priv->tx_reclaim_req++; - priv->tx_bd_r_index = i; - - spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags); -} - -static int qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv) -{ - if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, - priv->tx_bd_num)) { - qtnf_pcie_data_tx_reclaim(priv); - - if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, - priv->tx_bd_num)) { - pr_warn_ratelimited("reclaim full Tx queue\n"); - priv->tx_full_count++; - return 0; - } - } - - return 1; -} - -static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) -{ - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - dma_addr_t txbd_paddr, skb_paddr; - struct qtnf_tx_bd *txbd; - unsigned long flags; - int len, i; - u32 info; - int ret = 0; - - spin_lock_irqsave(&priv->tx0_lock, flags); - - if (!qtnf_tx_queue_ready(priv)) { - if (skb->dev) { - netif_tx_stop_all_queues(skb->dev); - priv->tx_stopped = 1; - } - - spin_unlock_irqrestore(&priv->tx0_lock, flags); - return NETDEV_TX_BUSY; - } - - i = priv->tx_bd_w_index; - priv->tx_skb[i] = skb; - len = skb->len; - - skb_paddr = pci_map_single(priv->pdev, skb->data, - skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(priv->pdev, skb_paddr)) { - pr_err("skb DMA mapping error: %pad\n", &skb_paddr); - ret = -ENOMEM; - goto tx_done; - } - - txbd = &priv->tx_bd_vbase[i]; - txbd->addr = cpu_to_le32(QTN_HOST_LO32(skb_paddr)); - txbd->addr_h = cpu_to_le32(QTN_HOST_HI32(skb_paddr)); - - info = (len & QTN_PCIE_TX_DESC_LEN_MASK) << QTN_PCIE_TX_DESC_LEN_SHIFT; - txbd->info = cpu_to_le32(info); - - /* sync up all descriptor updates before passing them to EP */ - dma_wmb(); - - /* write new TX descriptor to PCIE_RX_FIFO on EP */ - txbd_paddr = priv->tx_bd_pbase + i * sizeof(struct qtnf_tx_bd); - -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - writel(QTN_HOST_HI32(txbd_paddr), - PCIE_HDP_HOST_WR_DESC0_H(priv->pcie_reg_base)); -#endif - writel(QTN_HOST_LO32(txbd_paddr), - PCIE_HDP_HOST_WR_DESC0(priv->pcie_reg_base)); - - if (++i >= priv->tx_bd_num) - i = 0; - - priv->tx_bd_w_index = i; - -tx_done: - if (ret && skb) { - pr_err_ratelimited("drop skb\n"); - if (skb->dev) - skb->dev->stats.tx_dropped++; - dev_kfree_skb_any(skb); - } - - priv->tx_done_count++; - spin_unlock_irqrestore(&priv->tx0_lock, flags); - - qtnf_pcie_data_tx_reclaim(priv); - - return NETDEV_TX_OK; -} - -static int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb) -{ - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - int ret; - - ret = qtnf_shm_ipc_send(&priv->shm_ipc_ep_in, skb->data, skb->len); - - if (ret == -ETIMEDOUT) { - pr_err("EP firmware is dead\n"); - bus->fw_state = QTNF_FW_STATE_EP_DEAD; - } - - return ret; -} - -static irqreturn_t qtnf_interrupt(int irq, void *data) -{ - struct qtnf_bus *bus = (struct qtnf_bus *)data; - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - u32 status; - - priv->pcie_irq_count++; - status = readl(PCIE_HDP_INT_STATUS(priv->pcie_reg_base)); - - qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in); - qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_out); - - if (!(status & priv->pcie_irq_mask)) - goto irq_done; - - if (status & PCIE_HDP_INT_RX_BITS) - priv->pcie_irq_rx_count++; - - if (status & PCIE_HDP_INT_TX_BITS) - priv->pcie_irq_tx_count++; - - if (status & PCIE_HDP_INT_HHBM_UF) - priv->pcie_irq_uf_count++; - - if (status & PCIE_HDP_INT_RX_BITS) { - qtnf_dis_rxdone_irq(priv); - napi_schedule(&bus->mux_napi); - } - - if (status & PCIE_HDP_INT_TX_BITS) { - qtnf_dis_txdone_irq(priv); - tasklet_hi_schedule(&priv->reclaim_tq); - } - -irq_done: - /* H/W workaround: clean all bits, not only enabled */ - qtnf_non_posted_write(~0U, PCIE_HDP_INT_STATUS(priv->pcie_reg_base)); - - if (!priv->msi_enabled) - qtnf_deassert_intx(priv); - - return IRQ_HANDLED; -} - -static int qtnf_rx_data_ready(struct qtnf_pcie_bus_priv *priv) -{ - u16 index = priv->rx_bd_r_index; - struct qtnf_rx_bd *rxbd; - u32 descw; - - rxbd = &priv->rx_bd_vbase[index]; - descw = le32_to_cpu(rxbd->info); - - if (descw & QTN_TXDONE_MASK) - return 1; - - return 0; -} - -static int qtnf_rx_poll(struct napi_struct *napi, int budget) -{ - struct qtnf_bus *bus = container_of(napi, struct qtnf_bus, mux_napi); - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - struct net_device *ndev = NULL; - struct sk_buff *skb = NULL; - int processed = 0; - struct qtnf_rx_bd *rxbd; - dma_addr_t skb_paddr; - int consume; - u32 descw; - u32 psize; - u16 r_idx; - u16 w_idx; - int ret; - - while (processed < budget) { - - - if (!qtnf_rx_data_ready(priv)) - goto rx_out; - - r_idx = priv->rx_bd_r_index; - rxbd = &priv->rx_bd_vbase[r_idx]; - descw = le32_to_cpu(rxbd->info); - - skb = priv->rx_skb[r_idx]; - psize = QTN_GET_LEN(descw); - consume = 1; - - if (!(descw & QTN_TXDONE_MASK)) { - pr_warn("skip invalid rxbd[%d]\n", r_idx); - consume = 0; - } - - if (!skb) { - pr_warn("skip missing rx_skb[%d]\n", r_idx); - consume = 0; - } - - if (skb && (skb_tailroom(skb) < psize)) { - pr_err("skip packet with invalid length: %u > %u\n", - psize, skb_tailroom(skb)); - consume = 0; - } - - if (skb) { - skb_paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), - le32_to_cpu(rxbd->addr)); - pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE, - PCI_DMA_FROMDEVICE); - } - - if (consume) { - skb_put(skb, psize); - ndev = qtnf_classify_skb(bus, skb); - if (likely(ndev)) { - qtnf_update_rx_stats(ndev, skb); - skb->protocol = eth_type_trans(skb, ndev); - napi_gro_receive(napi, skb); - } else { - pr_debug("drop untagged skb\n"); - bus->mux_dev.stats.rx_dropped++; - dev_kfree_skb_any(skb); - } - } else { - if (skb) { - bus->mux_dev.stats.rx_dropped++; - dev_kfree_skb_any(skb); - } - } - - priv->rx_skb[r_idx] = NULL; - if (++r_idx >= priv->rx_bd_num) - r_idx = 0; - - priv->rx_bd_r_index = r_idx; - - /* repalce processed buffer by a new one */ - w_idx = priv->rx_bd_w_index; - while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, - priv->rx_bd_num) > 0) { - if (++w_idx >= priv->rx_bd_num) - w_idx = 0; - - ret = skb2rbd_attach(priv, w_idx); - if (ret) { - pr_err("failed to allocate new rx_skb[%d]\n", - w_idx); - break; - } - } - - processed++; - } - -rx_out: - if (processed < budget) { - napi_complete(napi); - qtnf_en_rxdone_irq(priv); - } - - return processed; -} - -static void -qtnf_pcie_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev) -{ - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - - tasklet_hi_schedule(&priv->reclaim_tq); -} - -static void qtnf_pcie_data_rx_start(struct qtnf_bus *bus) -{ - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - - qtnf_enable_hdp_irqs(priv); - napi_enable(&bus->mux_napi); -} - -static void qtnf_pcie_data_rx_stop(struct qtnf_bus *bus) -{ - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - - napi_disable(&bus->mux_napi); - qtnf_disable_hdp_irqs(priv); -} - -static const struct qtnf_bus_ops qtnf_pcie_bus_ops = { - /* control path methods */ - .control_tx = qtnf_pcie_control_tx, - - /* data path methods */ - .data_tx = qtnf_pcie_data_tx, - .data_tx_timeout = qtnf_pcie_data_tx_timeout, - .data_rx_start = qtnf_pcie_data_rx_start, - .data_rx_stop = qtnf_pcie_data_rx_stop, -}; - -static int qtnf_dbg_mps_show(struct seq_file *s, void *data) -{ - struct qtnf_bus *bus = dev_get_drvdata(s->private); - struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); - - seq_printf(s, "%d\n", priv->mps); - - return 0; -} - -static int qtnf_dbg_msi_show(struct seq_file *s, void *data) -{ - struct qtnf_bus *bus = dev_get_drvdata(s->private); - struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); - - seq_printf(s, "%u\n", priv->msi_enabled); - - return 0; -} - -static int qtnf_dbg_irq_stats(struct seq_file *s, void *data) -{ - struct qtnf_bus *bus = dev_get_drvdata(s->private); - struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); - u32 reg = readl(PCIE_HDP_INT_EN(priv->pcie_reg_base)); - u32 status; - - seq_printf(s, "pcie_irq_count(%u)\n", priv->pcie_irq_count); - seq_printf(s, "pcie_irq_tx_count(%u)\n", priv->pcie_irq_tx_count); - status = reg & PCIE_HDP_INT_TX_BITS; - seq_printf(s, "pcie_irq_tx_status(%s)\n", - (status == PCIE_HDP_INT_TX_BITS) ? "EN" : "DIS"); - seq_printf(s, "pcie_irq_rx_count(%u)\n", priv->pcie_irq_rx_count); - status = reg & PCIE_HDP_INT_RX_BITS; - seq_printf(s, "pcie_irq_rx_status(%s)\n", - (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS"); - seq_printf(s, "pcie_irq_uf_count(%u)\n", priv->pcie_irq_uf_count); - status = reg & PCIE_HDP_INT_HHBM_UF; - seq_printf(s, "pcie_irq_hhbm_uf_status(%s)\n", - (status == PCIE_HDP_INT_HHBM_UF) ? "EN" : "DIS"); - - return 0; -} - -static int qtnf_dbg_hdp_stats(struct seq_file *s, void *data) -{ - struct qtnf_bus *bus = dev_get_drvdata(s->private); - struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); - - seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count); - seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count); - seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done); - seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req); - - seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index); - seq_printf(s, "tx_bd_p_index(%u)\n", - readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) - & (priv->tx_bd_num - 1)); - seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index); - seq_printf(s, "tx queue len(%u)\n", - CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index, - priv->tx_bd_num)); - - seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index); - seq_printf(s, "rx_bd_p_index(%u)\n", - readl(PCIE_HDP_TX0DMA_CNT(priv->pcie_reg_base)) - & (priv->rx_bd_num - 1)); - seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index); - seq_printf(s, "rx alloc queue len(%u)\n", - CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, - priv->rx_bd_num)); - - return 0; -} - -static int qtnf_dbg_shm_stats(struct seq_file *s, void *data) -{ - struct qtnf_bus *bus = dev_get_drvdata(s->private); - struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); - - seq_printf(s, "shm_ipc_ep_in.tx_packet_count(%zu)\n", - priv->shm_ipc_ep_in.tx_packet_count); - seq_printf(s, "shm_ipc_ep_in.rx_packet_count(%zu)\n", - priv->shm_ipc_ep_in.rx_packet_count); - seq_printf(s, "shm_ipc_ep_out.tx_packet_count(%zu)\n", - priv->shm_ipc_ep_out.tx_timeout_count); - seq_printf(s, "shm_ipc_ep_out.rx_packet_count(%zu)\n", - priv->shm_ipc_ep_out.rx_packet_count); - - return 0; -} - -static int qtnf_ep_fw_send(struct qtnf_pcie_bus_priv *priv, uint32_t size, - int blk, const u8 *pblk, const u8 *fw) -{ - struct pci_dev *pdev = priv->pdev; - struct qtnf_bus *bus = pci_get_drvdata(pdev); - - struct qtnf_pcie_fw_hdr *hdr; - u8 *pdata; - - int hds = sizeof(*hdr); - struct sk_buff *skb = NULL; - int len = 0; - int ret; - - skb = __dev_alloc_skb(QTN_PCIE_FW_BUFSZ, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - skb->len = QTN_PCIE_FW_BUFSZ; - skb->dev = NULL; - - hdr = (struct qtnf_pcie_fw_hdr *)skb->data; - memcpy(hdr->boardflg, QTN_PCIE_BOARDFLG, strlen(QTN_PCIE_BOARDFLG)); - hdr->fwsize = cpu_to_le32(size); - hdr->seqnum = cpu_to_le32(blk); - - if (blk) - hdr->type = cpu_to_le32(QTN_FW_DSUB); - else - hdr->type = cpu_to_le32(QTN_FW_DBEGIN); - - pdata = skb->data + hds; - - len = QTN_PCIE_FW_BUFSZ - hds; - if (pblk >= (fw + size - len)) { - len = fw + size - pblk; - hdr->type = cpu_to_le32(QTN_FW_DEND); - } - - hdr->pktlen = cpu_to_le32(len); - memcpy(pdata, pblk, len); - hdr->crc = cpu_to_le32(~crc32(0, pdata, len)); - - ret = qtnf_pcie_data_tx(bus, skb); - - return (ret == NETDEV_TX_OK) ? len : 0; -} - -static int -qtnf_ep_fw_load(struct qtnf_pcie_bus_priv *priv, const u8 *fw, u32 fw_size) -{ - int blk_size = QTN_PCIE_FW_BUFSZ - sizeof(struct qtnf_pcie_fw_hdr); - int blk_count = fw_size / blk_size + ((fw_size % blk_size) ? 1 : 0); - const u8 *pblk = fw; - int threshold = 0; - int blk = 0; - int len; - - pr_debug("FW upload started: fw_addr=0x%p size=%d\n", fw, fw_size); - - while (blk < blk_count) { - if (++threshold > 10000) { - pr_err("FW upload failed: too many retries\n"); - return -ETIMEDOUT; - } - - len = qtnf_ep_fw_send(priv, fw_size, blk, pblk, fw); - if (len <= 0) - continue; - - if (!((blk + 1) & QTN_PCIE_FW_DLMASK) || - (blk == (blk_count - 1))) { - qtnf_set_state(&priv->bda->bda_rc_state, - QTN_RC_FW_SYNC); - if (qtnf_poll_state(&priv->bda->bda_ep_state, - QTN_EP_FW_SYNC, - QTN_FW_DL_TIMEOUT_MS)) { - pr_err("FW upload failed: SYNC timed out\n"); - return -ETIMEDOUT; - } - - qtnf_clear_state(&priv->bda->bda_ep_state, - QTN_EP_FW_SYNC); - - if (qtnf_is_state(&priv->bda->bda_ep_state, - QTN_EP_FW_RETRY)) { - if (blk == (blk_count - 1)) { - int last_round = - blk_count & QTN_PCIE_FW_DLMASK; - blk -= last_round; - pblk -= ((last_round - 1) * - blk_size + len); - } else { - blk -= QTN_PCIE_FW_DLMASK; - pblk -= QTN_PCIE_FW_DLMASK * blk_size; - } - - qtnf_clear_state(&priv->bda->bda_ep_state, - QTN_EP_FW_RETRY); - - pr_warn("FW upload retry: block #%d\n", blk); - continue; - } - - qtnf_pcie_data_tx_reclaim(priv); - } - - pblk += len; - blk++; - } - - pr_debug("FW upload completed: totally sent %d blocks\n", blk); - return 0; -} - -static void qtnf_fw_work_handler(struct work_struct *work) -{ - struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work); - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - struct pci_dev *pdev = priv->pdev; - const struct firmware *fw; - int ret; - u32 state = QTN_RC_FW_LOADRDY | QTN_RC_FW_QLINK; - - if (flashboot) { - state |= QTN_RC_FW_FLASHBOOT; - } else { - ret = request_firmware(&fw, bus->fwname, &pdev->dev); - if (ret < 0) { - pr_err("failed to get firmware %s\n", bus->fwname); - goto fw_load_fail; - } - } - - qtnf_set_state(&priv->bda->bda_rc_state, state); - - if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY, - QTN_FW_DL_TIMEOUT_MS)) { - pr_err("card is not ready\n"); - - if (!flashboot) - release_firmware(fw); - - goto fw_load_fail; - } - - qtnf_clear_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY); - - if (flashboot) { - pr_info("booting firmware from flash\n"); - } else { - pr_info("starting firmware upload: %s\n", bus->fwname); - - ret = qtnf_ep_fw_load(priv, fw->data, fw->size); - release_firmware(fw); - if (ret) { - pr_err("firmware upload error\n"); - goto fw_load_fail; - } - } - - if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_DONE, - QTN_FW_DL_TIMEOUT_MS)) { - pr_err("firmware bringup timed out\n"); - goto fw_load_fail; - } - - bus->fw_state = QTNF_FW_STATE_FW_DNLD_DONE; - pr_info("firmware is up and running\n"); - - if (qtnf_poll_state(&priv->bda->bda_ep_state, - QTN_EP_FW_QLINK_DONE, QTN_FW_QLINK_TIMEOUT_MS)) { - pr_err("firmware runtime failure\n"); - goto fw_load_fail; - } - - ret = qtnf_core_attach(bus); - if (ret) { - pr_err("failed to attach core\n"); - goto fw_load_fail; - } - - qtnf_debugfs_init(bus, DRV_NAME); - qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show); - qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show); - qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats); - qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats); - qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats); - - goto fw_load_exit; - -fw_load_fail: - bus->fw_state = QTNF_FW_STATE_DETACHED; - -fw_load_exit: - complete(&bus->firmware_init_complete); - put_device(&pdev->dev); -} - -static void qtnf_bringup_fw_async(struct qtnf_bus *bus) -{ - struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); - struct pci_dev *pdev = priv->pdev; - - get_device(&pdev->dev); - INIT_WORK(&bus->fw_work, qtnf_fw_work_handler); - schedule_work(&bus->fw_work); -} - -static void qtnf_reclaim_tasklet_fn(unsigned long data) -{ - struct qtnf_pcie_bus_priv *priv = (void *)data; - - qtnf_pcie_data_tx_reclaim(priv); - qtnf_en_txdone_irq(priv); -} - -static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct qtnf_pcie_bus_priv *pcie_priv; - struct qtnf_bus *bus; - int ret; - - bus = devm_kzalloc(&pdev->dev, - sizeof(*bus) + sizeof(*pcie_priv), GFP_KERNEL); - if (!bus) - return -ENOMEM; - - pcie_priv = get_bus_priv(bus); - - pci_set_drvdata(pdev, bus); - bus->bus_ops = &qtnf_pcie_bus_ops; - bus->dev = &pdev->dev; - bus->fw_state = QTNF_FW_STATE_RESET; - pcie_priv->pdev = pdev; - - strcpy(bus->fwname, QTN_PCI_PEARL_FW_NAME); - init_completion(&bus->firmware_init_complete); - mutex_init(&bus->bus_lock); - spin_lock_init(&pcie_priv->tx0_lock); - spin_lock_init(&pcie_priv->irq_lock); - spin_lock_init(&pcie_priv->tx_reclaim_lock); - - /* init stats */ - pcie_priv->tx_full_count = 0; - pcie_priv->tx_done_count = 0; - pcie_priv->pcie_irq_count = 0; - pcie_priv->pcie_irq_rx_count = 0; - pcie_priv->pcie_irq_tx_count = 0; - pcie_priv->pcie_irq_uf_count = 0; - pcie_priv->tx_reclaim_done = 0; - pcie_priv->tx_reclaim_req = 0; - - tasklet_init(&pcie_priv->reclaim_tq, qtnf_reclaim_tasklet_fn, - (unsigned long)pcie_priv); - - init_dummy_netdev(&bus->mux_dev); - netif_napi_add(&bus->mux_dev, &bus->mux_napi, - qtnf_rx_poll, 10); - - pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PEARL_PCIE"); - if (!pcie_priv->workqueue) { - pr_err("failed to alloc bus workqueue\n"); - ret = -ENODEV; - goto err_init; - } - - if (!pci_is_pcie(pdev)) { - pr_err("device %s is not PCI Express\n", pci_name(pdev)); - ret = -EIO; - goto err_base; - } - - qtnf_tune_pcie_mps(pcie_priv); - - ret = pcim_enable_device(pdev); - if (ret) { - pr_err("failed to init PCI device %x\n", pdev->device); - goto err_base; - } else { - pr_debug("successful init of PCI device %x\n", pdev->device); - } - -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); -#else - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); -#endif - if (ret) { - pr_err("PCIE DMA coherent mask init failed\n"); - goto err_base; - } - - pci_set_master(pdev); - qtnf_pcie_init_irq(pcie_priv); - - ret = qtnf_pcie_init_memory(pcie_priv); - if (ret < 0) { - pr_err("PCIE memory init failed\n"); - goto err_base; - } - - pci_save_state(pdev); - - ret = qtnf_pcie_init_shm_ipc(pcie_priv); - if (ret < 0) { - pr_err("PCIE SHM IPC init failed\n"); - goto err_base; - } - - ret = qtnf_pcie_init_xfer(pcie_priv); - if (ret) { - pr_err("PCIE xfer init failed\n"); - goto err_ipc; - } - - /* init default irq settings */ - qtnf_init_hdp_irqs(pcie_priv); - - /* start with disabled irqs */ - qtnf_disable_hdp_irqs(pcie_priv); - - ret = devm_request_irq(&pdev->dev, pdev->irq, &qtnf_interrupt, 0, - "qtnf_pcie_irq", (void *)bus); - if (ret) { - pr_err("failed to request pcie irq %d\n", pdev->irq); - goto err_xfer; - } - - qtnf_bringup_fw_async(bus); - - return 0; - -err_xfer: - qtnf_free_xfer_buffers(pcie_priv); - -err_ipc: - qtnf_pcie_free_shm_ipc(pcie_priv); - -err_base: - flush_workqueue(pcie_priv->workqueue); - destroy_workqueue(pcie_priv->workqueue); - netif_napi_del(&bus->mux_napi); - -err_init: - tasklet_kill(&pcie_priv->reclaim_tq); - pci_set_drvdata(pdev, NULL); - - return ret; -} - -static void qtnf_pcie_remove(struct pci_dev *pdev) -{ - struct qtnf_pcie_bus_priv *priv; - struct qtnf_bus *bus; - - bus = pci_get_drvdata(pdev); - if (!bus) - return; - - wait_for_completion(&bus->firmware_init_complete); - - if (bus->fw_state == QTNF_FW_STATE_ACTIVE || - bus->fw_state == QTNF_FW_STATE_EP_DEAD) - qtnf_core_detach(bus); - - priv = get_bus_priv(bus); - - netif_napi_del(&bus->mux_napi); - flush_workqueue(priv->workqueue); - destroy_workqueue(priv->workqueue); - tasklet_kill(&priv->reclaim_tq); - - qtnf_free_xfer_buffers(priv); - qtnf_debugfs_remove(bus); - - qtnf_pcie_free_shm_ipc(priv); - qtnf_reset_card(priv); -} - -#ifdef CONFIG_PM_SLEEP -static int qtnf_pcie_suspend(struct device *dev) -{ - return -EOPNOTSUPP; -} - -static int qtnf_pcie_resume(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -#ifdef CONFIG_PM_SLEEP -/* Power Management Hooks */ -static SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend, - qtnf_pcie_resume); -#endif - -static const struct pci_device_id qtnf_pcie_devid_table[] = { - { - PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - }, - { }, -}; - -MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table); - -static struct pci_driver qtnf_pcie_drv_data = { - .name = DRV_NAME, - .id_table = qtnf_pcie_devid_table, - .probe = qtnf_pcie_probe, - .remove = qtnf_pcie_remove, -#ifdef CONFIG_PM_SLEEP - .driver = { - .pm = &qtnf_pcie_pm_ops, - }, -#endif -}; - -static int __init qtnf_pcie_register(void) -{ - pr_info("register Quantenna QSR10g FullMAC PCIE driver\n"); - return pci_register_driver(&qtnf_pcie_drv_data); -} - -static void __exit qtnf_pcie_exit(void) -{ - pr_info("unregister Quantenna QSR10g FullMAC PCIE driver\n"); - pci_unregister_driver(&qtnf_pcie_drv_data); -} - -module_init(qtnf_pcie_register); -module_exit(qtnf_pcie_exit); - -MODULE_AUTHOR("Quantenna Communications"); -MODULE_DESCRIPTION("Quantenna QSR10g PCIe bus driver for 802.11 wireless LAN."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h deleted file mode 100644 index 397875a50fc2..000000000000 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2015-2016 Quantenna Communications, Inc. - * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * 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. - * - */ - -#ifndef _QTN_FMAC_PCIE_H_ -#define _QTN_FMAC_PCIE_H_ - -#include <linux/dma-mapping.h> -#include <linux/io.h> - -#include "pcie_regs_pearl.h" -#include "pcie_ipc.h" -#include "shm_ipc.h" - -struct bus; - -struct qtnf_pcie_bus_priv { - struct pci_dev *pdev; - - /* lock for irq configuration changes */ - spinlock_t irq_lock; - - /* lock for tx reclaim operations */ - spinlock_t tx_reclaim_lock; - /* lock for tx0 operations */ - spinlock_t tx0_lock; - u8 msi_enabled; - u8 tx_stopped; - int mps; - - struct workqueue_struct *workqueue; - struct tasklet_struct reclaim_tq; - - void __iomem *sysctl_bar; - void __iomem *epmem_bar; - void __iomem *dmareg_bar; - - struct qtnf_shm_ipc shm_ipc_ep_in; - struct qtnf_shm_ipc shm_ipc_ep_out; - - struct qtnf_pcie_bda __iomem *bda; - void __iomem *pcie_reg_base; - - u16 tx_bd_num; - u16 rx_bd_num; - - struct sk_buff **tx_skb; - struct sk_buff **rx_skb; - - struct qtnf_tx_bd *tx_bd_vbase; - dma_addr_t tx_bd_pbase; - - struct qtnf_rx_bd *rx_bd_vbase; - dma_addr_t rx_bd_pbase; - - dma_addr_t bd_table_paddr; - void *bd_table_vaddr; - u32 bd_table_len; - - u32 rx_bd_w_index; - u32 rx_bd_r_index; - - u32 tx_bd_w_index; - u32 tx_bd_r_index; - - u32 pcie_irq_mask; - - /* diagnostics stats */ - u32 pcie_irq_count; - u32 pcie_irq_rx_count; - u32 pcie_irq_tx_count; - u32 pcie_irq_uf_count; - u32 tx_full_count; - u32 tx_done_count; - u32 tx_reclaim_done; - u32 tx_reclaim_req; -}; - -#endif /* _QTN_FMAC_PCIE_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h b/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h index c4ad40d59085..1fe798a9a667 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h @@ -25,8 +25,22 @@ #define PCIE_DEVICE_ID_QTN_PEARL (0x0008) +#define QTN_REG_SYS_CTRL_CSR 0x14 +#define QTN_CHIP_ID_MASK 0xF0 +#define QTN_CHIP_ID_TOPAZ 0x40 +#define QTN_CHIP_ID_PEARL 0x50 +#define QTN_CHIP_ID_PEARL_B 0x60 +#define QTN_CHIP_ID_PEARL_C 0x70 + /* FW names */ #define QTN_PCI_PEARL_FW_NAME "qtn/fmac_qsr10g.img" +static inline unsigned int qtnf_chip_id_get(const void __iomem *regs_base) +{ + u32 board_rev = readl(regs_base + QTN_REG_SYS_CTRL_CSR); + + return board_rev & QTN_CHIP_ID_MASK; +} + #endif /* _QTN_HW_IDS_H_ */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index a567bc273ffc..9e7b8933d30c 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -957,6 +957,47 @@ static void rt2800_rate_from_status(struct skb_frame_desc *skbdesc, skbdesc->tx_rate_flags = flags; } +static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg) +{ + __le32 *txwi; + u32 word; + int wcid, ack, pid; + int tx_wcid, tx_ack, tx_pid, is_agg; + + /* + * This frames has returned with an IO error, + * so the status report is not intended for this + * frame. + */ + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + return false; + + wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); + ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); + pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE); + is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE); + + /* + * Validate if this TX status report is intended for + * this entry by comparing the WCID/ACK/PID fields. + */ + txwi = rt2800_drv_get_txwi(entry); + + word = rt2x00_desc_read(txwi, 1); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK); + tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); + + if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status report missed for queue %d entry %d\n", + entry->queue->qid, entry->entry_idx); + return false; + } + + return true; +} + void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, bool match) { @@ -1059,6 +1100,119 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, } EXPORT_SYMBOL_GPL(rt2800_txdone_entry); +void rt2800_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + u32 reg; + u8 qid; + bool match; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { + /* + * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is + * guaranteed to be one of the TX QIDs . + */ + qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + + if (unlikely(rt2x00queue_empty(queue))) { + rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { + rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n", + entry->entry_idx, qid); + break; + } + + match = rt2800_txdone_entry_check(entry, reg); + rt2800_txdone_entry(entry, reg, rt2800_drv_get_txwi(entry), match); + } +} +EXPORT_SYMBOL_GPL(rt2800_txdone); + +static inline bool rt2800_entry_txstatus_timeout(struct rt2x00_dev *rt2x00dev, + struct queue_entry *entry) +{ + bool ret; + unsigned long tout; + + if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; + + if (test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags)) + tout = msecs_to_jiffies(50); + else + tout = msecs_to_jiffies(2000); + + ret = time_after(jiffies, entry->last_action + tout); + if (unlikely(ret)) + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status timeout for entry %d in queue %d\n", + entry->entry_idx, entry->queue->qid); + return ret; +} + +bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + + if (!test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags)) { + unsigned long tout = msecs_to_jiffies(1000); + + if (time_before(jiffies, rt2x00dev->last_nostatus_check + tout)) + return false; + } + + rt2x00dev->last_nostatus_check = jiffies; + + tx_queue_for_each(rt2x00dev, queue) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + if (rt2800_entry_txstatus_timeout(rt2x00dev, entry)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(rt2800_txstatus_timeout); + +void rt2800_txdone_nostatus(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + + /* + * Process any trailing TX status reports for IO failures, + * we loop until we find the first non-IO error entry. This + * can either be a frame which is free, is being uploaded, + * or has completed the upload but didn't have an entry + * in the TX_STAT_FIFO register yet. + */ + tx_queue_for_each(rt2x00dev, queue) { + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; + + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags) || + rt2800_entry_txstatus_timeout(rt2x00dev, entry)) + rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); + else + break; + } + } +} +EXPORT_SYMBOL_GPL(rt2800_txdone_nostatus); + static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev, unsigned int index) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 51d9c2a932cc..0dff2c7b3010 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -195,6 +195,9 @@ void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *tx void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, bool match); +void rt2800_txdone(struct rt2x00_dev *rt2x00dev); +void rt2800_txdone_nostatus(struct rt2x00_dev *rt2x00dev); +bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_clear_beacon(struct queue_entry *entry); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c index e1a7ed7e4892..ddb88cfeace2 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c @@ -175,161 +175,6 @@ static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); } -static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) -{ - __le32 *txwi; - u32 word; - int wcid, tx_wcid; - - wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); - - txwi = rt2800_drv_get_txwi(entry); - word = rt2x00_desc_read(txwi, 1); - tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); - - return (tx_wcid == wcid); -} - -static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * rt2800pci hardware might reorder frames when exchanging traffic - * with multiple BA enabled STAs. - * - * For example, a tx queue - * [ STA1 | STA2 | STA1 | STA2 ] - * can result in tx status reports - * [ STA1 | STA1 | STA2 | STA2 ] - * when the hw decides to aggregate the frames for STA1 into one AMPDU. - * - * To mitigate this effect, associate the tx status to the first frame - * in the tx queue with a matching wcid. - */ - if (rt2800mmio_txdone_entry_check(entry, status) && - !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} - -static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * Find the first frame without tx status and assign this status to it - * regardless if it matches or not. - */ - if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} -static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, - void *data) -{ - if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - rt2800_txdone_entry(entry, entry->status, - rt2800mmio_get_txwi(entry), true); - return false; - } - - /* No more frames to release */ - return true; -} - -static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - u32 status; - u8 qid; - int max_tx_done = 16; - - while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { - qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); - if (unlikely(qid >= QID_RX)) { - /* - * Unknown queue, this shouldn't happen. Just drop - * this tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", - qid); - break; - } - - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - if (unlikely(queue == NULL)) { - /* - * The queue is NULL, this shouldn't happen. Stop - * processing here and drop the tx status - */ - rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", - qid); - break; - } - - if (unlikely(rt2x00queue_empty(queue))) { - /* - * The queue is empty. Stop processing here - * and drop the tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); - break; - } - - /* - * Let's associate this tx status with the first - * matching frame. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800mmio_txdone_find_entry)) { - /* - * We cannot match the tx status to any frame, so just - * use the first one. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800mmio_txdone_match_first)) { - rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", - qid); - break; - } - } - - /* - * Release all frames with a valid tx status. - */ - rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, NULL, - rt2800mmio_txdone_release_entries); - - if (--max_tx_done == 0) - break; - } - - return !max_tx_done; -} - static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, struct rt2x00_field32 irq_field) { @@ -346,20 +191,6 @@ static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, spin_unlock_irq(&rt2x00dev->irqmask_lock); } -void rt2800mmio_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2800mmio_txdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - - /* - * No need to enable the tx status interrupt here as we always - * leave it enabled to minimize the possibility of a tx status - * register overflow. See comment in interrupt handler. - */ -} -EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); - void rt2800mmio_pretbtt_tasklet(unsigned long data) { struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; @@ -424,12 +255,26 @@ void rt2800mmio_autowake_tasklet(unsigned long data) } EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); -static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) +static void rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) +{ + bool timeout = false; + + while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || + (timeout = rt2800_txstatus_timeout(rt2x00dev))) { + + rt2800_txdone(rt2x00dev); + + if (timeout) + rt2800_txdone_nostatus(rt2x00dev); + } +} + +static bool rt2800mmio_fetch_txstatus(struct rt2x00_dev *rt2x00dev) { u32 status; - int i; + bool more = false; - /* + /* FIXEME: rewrite this comment * The TX_FIFO_STATUS interrupt needs special care. We should * read TX_STA_FIFO but we should do it immediately as otherwise * the register can overflow and we would lose status reports. @@ -440,28 +285,36 @@ static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) * because we can schedule the tasklet multiple times (when the * interrupt fires again during tx status processing). * - * Furthermore we don't disable the TX_FIFO_STATUS - * interrupt here but leave it enabled so that the TX_STA_FIFO - * can also be read while the tx status tasklet gets executed. - * - * Since we have only one producer and one consumer we don't + * txstatus tasklet is called with INT_SOURCE_CSR_TX_FIFO_STATUS + * disabled so have only one producer and one consumer - we don't * need to lock the kfifo. */ - for (i = 0; i < rt2x00dev->tx->limit; i++) { + while (!kfifo_is_full(&rt2x00dev->txstatus_fifo)) { status = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO); - if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) break; - if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) { - rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); - break; - } + kfifo_put(&rt2x00dev->txstatus_fifo, status); + more = true; } - /* Schedule the tasklet for processing the tx status. */ - tasklet_schedule(&rt2x00dev->txstatus_tasklet); + return more; +} + +void rt2800mmio_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + + do { + rt2800mmio_txdone(rt2x00dev); + + } while (rt2800mmio_fetch_txstatus(rt2x00dev)); + + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, + INT_SOURCE_CSR_TX_FIFO_STATUS); } +EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) { @@ -486,11 +339,8 @@ irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) mask = ~reg; if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { - rt2800mmio_txstatus_interrupt(rt2x00dev); - /* - * Never disable the TX_FIFO_STATUS interrupt. - */ - rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2800mmio_fetch_txstatus(rt2x00dev); + tasklet_schedule(&rt2x00dev->txstatus_tasklet); } if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) @@ -616,6 +466,53 @@ void rt2800mmio_kick_queue(struct data_queue *queue) } EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); +void rt2800mmio_flush_queue(struct data_queue *queue, bool drop) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + bool tx_queue = false; + unsigned int i; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + tx_queue = true; + break; + case QID_RX: + break; + default: + return; + } + + for (i = 0; i < 5; i++) { + /* + * Check if the driver is already done, otherwise we + * have to sleep a little while to give the driver/hw + * the oppurtunity to complete interrupt process itself. + */ + if (rt2x00queue_empty(queue)) + break; + + /* + * For TX queues schedule completion tasklet to catch + * tx status timeouts, othewise just wait. + */ + if (tx_queue) { + tasklet_disable(&rt2x00dev->txstatus_tasklet); + rt2800mmio_txdone(rt2x00dev); + tasklet_enable(&rt2x00dev->txstatus_tasklet); + } + + /* + * Wait for a little while to give the driver + * the oppurtunity to recover itself. + */ + msleep(50); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_flush_queue); + void rt2800mmio_stop_queue(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h index b63312ce3f27..3a513273f414 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h @@ -148,6 +148,7 @@ void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, /* Queue handlers */ void rt2800mmio_start_queue(struct data_queue *queue); void rt2800mmio_kick_queue(struct data_queue *queue); +void rt2800mmio_flush_queue(struct data_queue *queue, bool drop); void rt2800mmio_stop_queue(struct data_queue *queue); void rt2800mmio_queue_init(struct data_queue *queue); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c index 71b1affc3885..0291441ac548 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -364,7 +364,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .start_queue = rt2800mmio_start_queue, .kick_queue = rt2800mmio_kick_queue, .stop_queue = rt2800mmio_stop_queue, - .flush_queue = rt2x00mmio_flush_queue, + .flush_queue = rt2800mmio_flush_queue, .write_tx_desc = rt2800mmio_write_tx_desc, .write_tx_data = rt2800_write_tx_data, .write_beacon = rt2800_write_beacon, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 98a7313fea4a..19eabf16147b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -116,35 +116,6 @@ static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev) return false; } -static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry) -{ - bool tout; - - if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - return false; - - tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(500)); - if (unlikely(tout)) - rt2x00_dbg(entry->queue->rt2x00dev, - "TX status timeout for entry %d in queue %d\n", - entry->entry_idx, entry->queue->qid); - return tout; - -} - -static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - - tx_queue_for_each(rt2x00dev, queue) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - if (rt2800usb_entry_txstatus_timeout(entry)) - return true; - } - return false; -} - #define TXSTATUS_READ_INTERVAL 1000000 static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, @@ -171,7 +142,7 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, } /* Check if there is any entry that timedout waiting on TX status */ - if (rt2800usb_txstatus_timeout(rt2x00dev)) + if (rt2800_txstatus_timeout(rt2x00dev)) queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); if (rt2800usb_txstatus_pending(rt2x00dev)) { @@ -501,123 +472,17 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry) /* * TX control handlers */ -static bool rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) -{ - __le32 *txwi; - u32 word; - int wcid, ack, pid; - int tx_wcid, tx_ack, tx_pid, is_agg; - - /* - * This frames has returned with an IO error, - * so the status report is not intended for this - * frame. - */ - if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) - return false; - - wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); - ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); - pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE); - is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE); - - /* - * Validate if this TX status report is intended for - * this entry by comparing the WCID/ACK/PID fields. - */ - txwi = rt2800usb_get_txwi(entry); - - word = rt2x00_desc_read(txwi, 1); - tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); - tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK); - tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); - - if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { - rt2x00_dbg(entry->queue->rt2x00dev, - "TX status report missed for queue %d entry %d\n", - entry->queue->qid, entry->entry_idx); - return false; - } - - return true; -} - -static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - u32 reg; - u8 qid; - bool match; - - while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { - /* - * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is - * guaranteed to be one of the TX QIDs . - */ - qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - - if (unlikely(rt2x00queue_empty(queue))) { - rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); - break; - } - - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - - if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { - rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n", - entry->entry_idx, qid); - break; - } - - match = rt2800usb_txdone_entry_check(entry, reg); - rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry), match); - } -} - -static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - - /* - * Process any trailing TX status reports for IO failures, - * we loop until we find the first non-IO error entry. This - * can either be a frame which is free, is being uploaded, - * or has completed the upload but didn't have an entry - * in the TX_STAT_FIFO register yet. - */ - tx_queue_for_each(rt2x00dev, queue) { - while (!rt2x00queue_empty(queue)) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - break; - - if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags) || - rt2800usb_entry_txstatus_timeout(entry)) - rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); - else - break; - } - } -} - static void rt2800usb_work_txdone(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, txdone_work); while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || - rt2800usb_txstatus_timeout(rt2x00dev)) { + rt2800_txstatus_timeout(rt2x00dev)) { - rt2800usb_txdone(rt2x00dev); + rt2800_txdone(rt2x00dev); - rt2800usb_txdone_nostatus(rt2x00dev); + rt2800_txdone_nostatus(rt2x00dev); /* * The hw may delay sending the packet after DMA complete diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index a279a4363bc1..4b1744e9fb78 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -665,6 +665,7 @@ enum rt2x00_state_flags { DEVICE_STATE_STARTED, DEVICE_STATE_ENABLED_RADIO, DEVICE_STATE_SCANNING, + DEVICE_STATE_FLUSHING, /* * Driver configuration @@ -980,6 +981,8 @@ struct rt2x00_dev { */ DECLARE_KFIFO_PTR(txstatus_fifo, u32); + unsigned long last_nostatus_check; + /* * Timer to ensure tx status reports are read (rt2800usb). */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c index acc399b5574e..61ba573e8bf1 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c @@ -464,11 +464,7 @@ static ssize_t rt2x00debug_read_##__name(struct file *file, \ \ size = sprintf(line, __format, value); \ \ - if (copy_to_user(buf, line, size)) \ - return -EFAULT; \ - \ - *offset += size; \ - return size; \ + return simple_read_from_buffer(buf, length, offset, line, size); \ } #define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \ @@ -545,11 +541,7 @@ static ssize_t rt2x00debug_read_dev_flags(struct file *file, size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags); - if (copy_to_user(buf, line, size)) - return -EFAULT; - - *offset += size; - return size; + return simple_read_from_buffer(buf, length, offset, line, size); } static const struct file_operations rt2x00debug_fop_dev_flags = { @@ -574,11 +566,7 @@ static ssize_t rt2x00debug_read_cap_flags(struct file *file, size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags); - if (copy_to_user(buf, line, size)) - return -EFAULT; - - *offset += size; - return size; + return simple_read_from_buffer(buf, length, offset, line, size); } static const struct file_operations rt2x00debug_fop_cap_flags = { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c index fa2fd64084ac..2825560e2424 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c @@ -720,8 +720,12 @@ void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; + set_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags); + tx_queue_for_each(rt2x00dev, queue) rt2x00queue_flush_queue(queue, drop); + + clear_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags); } EXPORT_SYMBOL_GPL(rt2x00mac_flush); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c index 710e9641552e..92ddc19e7bf7 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c @@ -113,6 +113,7 @@ int rt2x00queue_map_txskb(struct queue_entry *entry) return -ENOMEM; skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; + rt2x00lib_dmadone(entry); return 0; } EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); @@ -1038,6 +1039,7 @@ void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev) */ tx_queue_for_each(rt2x00dev, queue) rt2x00queue_start_queue(queue); + rt2x00dev->last_nostatus_check = jiffies; rt2x00queue_start_queue(rt2x00dev->rx); } diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c index c2d5b495c179..c089540116fa 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/leds.c @@ -146,7 +146,7 @@ static int rtl8187_register_led(struct ieee80211_hw *dev, led->dev = dev; led->ledpin = ledpin; led->is_radio = is_radio; - strncpy(led->name, name, sizeof(led->name)); + strlcpy(led->name, name, sizeof(led->name)); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 505ab1b055ff..73f6fc0d4a01 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6231,6 +6231,8 @@ static const struct usb_device_id dev_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3308, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8192cu_fops}, /* Currently untested 8188 series devices */ +{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x018a, 0xff, 0xff, 0xff), + .driver_info = (unsigned long)&rtl8192cu_fops}, {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8191, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8192cu_fops}, {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8170, 0xff, 0xff, 0xff), diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c index b026e80940a4..6fbf8845a2ab 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c @@ -1324,13 +1324,13 @@ bool exhalbtc_initlize_variables_wifi_only(struct rtl_priv *rtlpriv) switch (rtlpriv->rtlhal.interface) { case INTF_PCI: - wifionly_cfg->chip_interface = BTC_INTF_PCI; + wifionly_cfg->chip_interface = WIFIONLY_INTF_PCI; break; case INTF_USB: - wifionly_cfg->chip_interface = BTC_INTF_USB; + wifionly_cfg->chip_interface = WIFIONLY_INTF_USB; break; default: - wifionly_cfg->chip_interface = BTC_INTF_UNKNOWN; + wifionly_cfg->chip_interface = WIFIONLY_INTF_UNKNOWN; break; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c index 988d5ac57d02..cfc8762c55f4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c @@ -951,12 +951,8 @@ static bool _rtl88ee_init_mac(struct ieee80211_hw *hw) static void _rtl88ee_hw_configure(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 reg_bw_opmode; - u32 reg_ratr, reg_prsr; + u32 reg_prsr; - reg_bw_opmode = BW_OPMODE_20MHZ; - reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG | - RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS; reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; rtl_write_dword(rtlpriv, REG_RRSR, reg_prsr); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c index 545115db507e..f783e4a8083d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c @@ -799,11 +799,9 @@ static void _rtl8723e_hw_configure(struct ieee80211_hw *hw) struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); u8 reg_bw_opmode; - u32 reg_ratr, reg_prsr; + u32 reg_prsr; reg_bw_opmode = BW_OPMODE_20MHZ; - reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG | - RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS; reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8); diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 01edf960ff3c..182b06629371 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -282,10 +282,8 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) struct rsi_hw *adapter = common->priv; struct ieee80211_vif *vif; struct ieee80211_tx_info *info; - struct skb_info *tx_params; struct ieee80211_bss_conf *bss; int status = -EINVAL; - u8 header_size; if (!skb) return 0; @@ -297,8 +295,6 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) goto err; vif = info->control.vif; bss = &vif->bss_conf; - tx_params = (struct skb_info *)info->driver_data; - header_size = tx_params->internal_hdr_size; if (((vif->type == NL80211_IFTYPE_STATION) || (vif->type == NL80211_IFTYPE_P2P_CLIENT)) && diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 4e510cbe0a89..e56fc83faf0e 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -924,7 +924,7 @@ static int rsi_hal_key_config(struct ieee80211_hw *hw, if (status) return status; - if (vif->type == NL80211_IFTYPE_STATION && key->key && + if (vif->type == NL80211_IFTYPE_STATION && (key->cipher == WLAN_CIPHER_SUITE_WEP104 || key->cipher == WLAN_CIPHER_SUITE_WEP40)) { if (!rsi_send_block_unblock_frame(adapter->priv, false)) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index c0a163e40402..f360690396dd 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -266,15 +266,17 @@ static void rsi_rx_done_handler(struct urb *urb) if (urb->status) goto out; - if (urb->actual_length <= 0) { - rsi_dbg(INFO_ZONE, "%s: Zero length packet\n", __func__); + if (urb->actual_length <= 0 || + urb->actual_length > rx_cb->rx_skb->len) { + rsi_dbg(INFO_ZONE, "%s: Invalid packet length = %d\n", + __func__, urb->actual_length); goto out; } if (skb_queue_len(&dev->rx_q) >= RSI_MAX_RX_PKTS) { rsi_dbg(INFO_ZONE, "Max RX packets reached\n"); goto out; } - skb_put(rx_cb->rx_skb, urb->actual_length); + skb_trim(rx_cb->rx_skb, urb->actual_length); skb_queue_tail(&dev->rx_q, rx_cb->rx_skb); rsi_set_event(&dev->rx_thread.event); @@ -308,6 +310,7 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) if (!skb) return -ENOMEM; skb_reserve(skb, MAX_DWORD_ALIGN_BYTES); + skb_put(skb, RSI_MAX_RX_USB_PKT_SIZE - MAX_DWORD_ALIGN_BYTES); dword_align_bytes = (unsigned long)skb->data & 0x3f; if (dword_align_bytes > 0) skb_push(skb, dword_align_bytes); @@ -319,7 +322,7 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) usb_rcvbulkpipe(dev->usbdev, dev->bulkin_endpoint_addr[ep_num - 1]), urb->transfer_buffer, - RSI_MAX_RX_USB_PKT_SIZE, + skb->len, rsi_rx_done_handler, rx_cb); diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index d9ff3b8be86e..60f1f286b030 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -75,7 +75,6 @@ static inline int rsi_kill_thread(struct rsi_thread *handle) atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - wait_for_completion(&handle->completion); return kthread_stop(handle->task); } diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c index f7b1b0062db3..8c800ef23159 100644 --- a/drivers/net/wireless/st/cw1200/txrx.c +++ b/drivers/net/wireless/st/cw1200/txrx.c @@ -624,9 +624,9 @@ cw1200_tx_h_bt(struct cw1200_common *priv, priority = WSM_EPTA_PRIORITY_ACTION; else if (ieee80211_is_mgmt(t->hdr->frame_control)) priority = WSM_EPTA_PRIORITY_MGT; - else if ((wsm->queue_id == WSM_QUEUE_VOICE)) + else if (wsm->queue_id == WSM_QUEUE_VOICE) priority = WSM_EPTA_PRIORITY_VOICE; - else if ((wsm->queue_id == WSM_QUEUE_VIDEO)) + else if (wsm->queue_id == WSM_QUEUE_VIDEO) priority = WSM_EPTA_PRIORITY_VIDEO; else priority = WSM_EPTA_PRIORITY_DATA; |