diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-05-29 14:50:45 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-05-29 14:50:45 -0400 |
commit | 531efffc3e3ff52d732c8fa6f32e084e9c447612 (patch) | |
tree | 79c301b2c8b5400a39c95e034764cf7a208a16a2 /drivers | |
parent | bbf71a8f35cf52e97ba129401f82ccefd249c757 (diff) | |
parent | b571a69745dc90aec3d0505a7ceac2702a93861b (diff) |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
Diffstat (limited to 'drivers')
70 files changed, 2907 insertions, 546 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 9b20d9ee2719..7f702fe3ecc2 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2369,6 +2369,9 @@ ath5k_tx_complete_poll_work(struct work_struct *work) int i; bool needreset = false; + if (!test_bit(ATH_STAT_STARTED, ah->status)) + return; + mutex_lock(&ah->lock); for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { @@ -2676,6 +2679,7 @@ done: mmiowb(); mutex_unlock(&ah->lock); + set_bit(ATH_STAT_STARTED, ah->status); ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work, msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); @@ -2737,6 +2741,7 @@ void ath5k_stop(struct ieee80211_hw *hw) ath5k_stop_tasklets(ah); + clear_bit(ATH_STAT_STARTED, ah->status); cancel_delayed_work_sync(&ah->tx_complete_work); if (!ath5k_modparam_no_hw_rfkill_switch) diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h index 0c2ac0c6dc89..e85a8b076c22 100644 --- a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h @@ -233,9 +233,9 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x00009d10, 0x01834061}, {0x00009d14, 0x00c00400}, {0x00009d18, 0x00000000}, - {0x00009e08, 0x0078230c}, - {0x00009e24, 0x990bb515}, - {0x00009e28, 0x126f0000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x9907b515}, + {0x00009e28, 0x126f0600}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, @@ -337,7 +337,7 @@ static const u32 ar9565_1p0_baseband_core[][2] = { static const u32 ar9565_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a800d}, + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8009}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x63c640da}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x09143c81}, @@ -345,9 +345,9 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = { {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, - {0x00009e04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, - {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8}, - {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, + {0x00009e04, 0x00802020, 0x00802020, 0x00142020, 0x00142020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, @@ -450,6 +450,8 @@ static const u32 ar9565_1p0_soc_postamble[][5] = { static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { /* Addr allmodes */ + {0x00004050, 0x00300300}, + {0x0000406c, 0x00100000}, {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, @@ -498,27 +500,27 @@ static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, + {0x0000a0c0, 0x00bf00a0}, + {0x0000a0c4, 0x11a011a1}, + {0x0000a0c8, 0x11be11bf}, + {0x0000a0cc, 0x11bc11bd}, + {0x0000a0d0, 0x22632264}, + {0x0000a0d4, 0x22612262}, + {0x0000a0d8, 0x227f2260}, + {0x0000a0dc, 0x4322227e}, + {0x0000a0e0, 0x43204321}, + {0x0000a0e4, 0x433e433f}, + {0x0000a0e8, 0x4462433d}, + {0x0000a0ec, 0x44604461}, + {0x0000a0f0, 0x447e447f}, + {0x0000a0f4, 0x5582447d}, + {0x0000a0f8, 0x55805581}, + {0x0000a0fc, 0x559e559f}, + {0x0000a100, 0x66816682}, + {0x0000a104, 0x669f6680}, + {0x0000a108, 0x669d669e}, + {0x0000a10c, 0x77627763}, + {0x0000a110, 0x77607761}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, @@ -530,27 +532,27 @@ static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, + {0x0000a140, 0x00bf00a0}, + {0x0000a144, 0x11a011a1}, + {0x0000a148, 0x11be11bf}, + {0x0000a14c, 0x11bc11bd}, + {0x0000a150, 0x22632264}, + {0x0000a154, 0x22612262}, + {0x0000a158, 0x227f2260}, + {0x0000a15c, 0x4322227e}, + {0x0000a160, 0x43204321}, + {0x0000a164, 0x433e433f}, + {0x0000a168, 0x4462433d}, + {0x0000a16c, 0x44604461}, + {0x0000a170, 0x447e447f}, + {0x0000a174, 0x5582447d}, + {0x0000a178, 0x55805581}, + {0x0000a17c, 0x559e559f}, + {0x0000a180, 0x66816682}, + {0x0000a184, 0x669f6680}, + {0x0000a188, 0x669d669e}, + {0x0000a18c, 0x77e677e7}, + {0x0000a190, 0x77e477e5}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, @@ -770,7 +772,7 @@ static const u32 ar9565_1p0_Modes_lowest_ob_db_tx_gain_table[][5] = { static const u32 ar9565_1p0_pciephy_clkreq_disable_L1[][2] = { /* Addr allmodes */ - {0x00018c00, 0x18213ede}, + {0x00018c00, 0x18212ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003780c}, }; @@ -889,8 +891,8 @@ static const u32 ar9565_1p0_common_wo_xlna_rx_gain_table[][2] = { {0x0000a180, 0x66816682}, {0x0000a184, 0x669f6680}, {0x0000a188, 0x669d669e}, - {0x0000a18c, 0x77627763}, - {0x0000a190, 0x77607761}, + {0x0000a18c, 0x77e677e7}, + {0x0000a190, 0x77e477e5}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, @@ -1114,7 +1116,7 @@ static const u32 ar9565_1p0_modes_high_ob_db_tx_gain_table[][5] = { {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050df, 0x000050df}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, {0x0000a508, 0x0b022220, 0x0b022220, 0x08000004, 0x08000004}, @@ -1140,13 +1142,13 @@ static const u32 ar9565_1p0_modes_high_ob_db_tx_gain_table[][5] = { {0x0000a558, 0x69027f56, 0x69027f56, 0x53001ce5, 0x53001ce5}, {0x0000a55c, 0x6d029f56, 0x6d029f56, 0x57001ce9, 0x57001ce9}, {0x0000a560, 0x73049f56, 0x73049f56, 0x5b001ceb, 0x5b001ceb}, - {0x0000a564, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x7804ff56, 0x7804ff56, 0x5d001eec, 0x5d001eec}, + {0x0000a564, 0x7804ff56, 0x7804ff56, 0x60001cf0, 0x60001cf0}, + {0x0000a568, 0x7804ff56, 0x7804ff56, 0x61001cf1, 0x61001cf1}, + {0x0000a56c, 0x7804ff56, 0x7804ff56, 0x62001cf2, 0x62001cf2}, + {0x0000a570, 0x7804ff56, 0x7804ff56, 0x63001cf3, 0x63001cf3}, + {0x0000a574, 0x7804ff56, 0x7804ff56, 0x64001cf4, 0x64001cf4}, + {0x0000a578, 0x7804ff56, 0x7804ff56, 0x66001ff6, 0x66001ff6}, + {0x0000a57c, 0x7804ff56, 0x7804ff56, 0x66001ff6, 0x66001ff6}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1174,7 +1176,7 @@ static const u32 ar9565_1p0_modes_high_power_tx_gain_table[][5] = { {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050df, 0x000050df}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, @@ -1200,13 +1202,13 @@ static const u32 ar9565_1p0_modes_high_power_tx_gain_table[][5] = { {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec}, + {0x0000a564, 0x7504ff56, 0x7504ff56, 0x59001cf0, 0x59001cf0}, + {0x0000a568, 0x7504ff56, 0x7504ff56, 0x5a001cf1, 0x5a001cf1}, + {0x0000a56c, 0x7504ff56, 0x7504ff56, 0x5b001cf2, 0x5b001cf2}, + {0x0000a570, 0x7504ff56, 0x7504ff56, 0x5c001cf3, 0x5c001cf3}, + {0x0000a574, 0x7504ff56, 0x7504ff56, 0x5d001cf4, 0x5d001cf4}, + {0x0000a578, 0x7504ff56, 0x7504ff56, 0x5f001ff6, 0x5f001ff6}, + {0x0000a57c, 0x7504ff56, 0x7504ff56, 0x5f001ff6, 0x5f001ff6}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e6a30958e944..a1630d1ee3c9 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -226,13 +226,13 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) goto work; - ath9k_set_beacon(sc); - if (ah->opmode == NL80211_IFTYPE_STATION && test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { spin_lock_irqsave(&sc->sc_pm_lock, flags); sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + } else { + ath9k_set_beacon(sc); } work: ath_restart_work(sc); @@ -1331,6 +1331,7 @@ static int ath9k_sta_add(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_node *an = (struct ath_node *) sta->drv_priv; struct ieee80211_key_conf ps_key = { }; + int key; ath_node_attach(sc, sta, vif); @@ -1338,7 +1339,9 @@ static int ath9k_sta_add(struct ieee80211_hw *hw, vif->type != NL80211_IFTYPE_AP_VLAN) return 0; - an->ps_key = ath_key_config(common, vif, sta, &ps_key); + key = ath_key_config(common, vif, sta, &ps_key); + if (key > 0) + an->ps_key = key; return 0; } @@ -1355,6 +1358,7 @@ static void ath9k_del_ps_key(struct ath_softc *sc, return; ath_key_delete(common, &ps_key); + an->ps_key = 0; } static int ath9k_sta_remove(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 523355b87659..f7c70b3a6ea9 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1728,6 +1728,25 @@ drop_recycle_buffer: sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); } +void b43_dma_handle_rx_overflow(struct b43_dmaring *ring) +{ + int current_slot, previous_slot; + + B43_WARN_ON(ring->tx); + + /* Device has filled all buffers, drop all packets and let TCP + * decrease speed. + * Decrement RX index by one will let the device to see all slots + * as free again + */ + /* + *TODO: How to increase rx_drop in mac80211? + */ + current_slot = ring->ops->get_current_rxslot(ring); + previous_slot = prev_slot(ring, current_slot); + ring->ops->set_current_rxslot(ring, previous_slot); +} + void b43_dma_rx(struct b43_dmaring *ring) { const struct b43_dma_ops *ops = ring->ops; diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index 9fdd1983079c..df8c8cdcbdb5 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -9,7 +9,7 @@ /* DMA-Interrupt reasons. */ #define B43_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ | (1 << 14) | (1 << 15)) -#define B43_DMAIRQ_NONFATALMASK (1 << 13) +#define B43_DMAIRQ_RDESC_UFLOW (1 << 13) #define B43_DMAIRQ_RX_DONE (1 << 16) /*** 32-bit DMA Engine. ***/ @@ -295,6 +295,8 @@ int b43_dma_tx(struct b43_wldev *dev, void b43_dma_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status); +void b43_dma_handle_rx_overflow(struct b43_dmaring *ring); + void b43_dma_rx(struct b43_dmaring *ring); void b43_dma_direct_fifo_rx(struct b43_wldev *dev, diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index d377f77d30b5..6dd07e2ec595 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1902,30 +1902,18 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) } } - if (unlikely(merged_dma_reason & (B43_DMAIRQ_FATALMASK | - B43_DMAIRQ_NONFATALMASK))) { - if (merged_dma_reason & B43_DMAIRQ_FATALMASK) { - b43err(dev->wl, "Fatal DMA error: " - "0x%08X, 0x%08X, 0x%08X, " - "0x%08X, 0x%08X, 0x%08X\n", - dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3], - dma_reason[4], dma_reason[5]); - b43err(dev->wl, "This device does not support DMA " + if (unlikely(merged_dma_reason & (B43_DMAIRQ_FATALMASK))) { + b43err(dev->wl, + "Fatal DMA error: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); + b43err(dev->wl, "This device does not support DMA " "on your system. It will now be switched to PIO.\n"); - /* Fall back to PIO transfers if we get fatal DMA errors! */ - dev->use_pio = true; - b43_controller_restart(dev, "DMA error"); - return; - } - if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) { - b43err(dev->wl, "DMA error: " - "0x%08X, 0x%08X, 0x%08X, " - "0x%08X, 0x%08X, 0x%08X\n", - dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3], - dma_reason[4], dma_reason[5]); - } + /* Fall back to PIO transfers if we get fatal DMA errors! */ + dev->use_pio = true; + b43_controller_restart(dev, "DMA error"); + return; } if (unlikely(reason & B43_IRQ_UCODE_DEBUG)) @@ -1944,6 +1932,11 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) handle_irq_noise(dev); /* Check the DMA reason registers for received data. */ + if (dma_reason[0] & B43_DMAIRQ_RDESC_UFLOW) { + if (B43_DEBUG) + b43warn(dev->wl, "RX descriptor underrun\n"); + b43_dma_handle_rx_overflow(dev->dma.rx_ring); + } if (dma_reason[0] & B43_DMAIRQ_RX_DONE) { if (b43_using_pio_transfers(dev)) b43_pio_rx(dev->pio.rx_queue); @@ -2001,7 +1994,7 @@ static irqreturn_t b43_do_interrupt(struct b43_wldev *dev) return IRQ_NONE; dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON) - & 0x0001DC00; + & 0x0001FC00; dev->dma_reason[1] = b43_read32(dev, B43_MMIO_DMA1_REASON) & 0x0000DC00; dev->dma_reason[2] = b43_read32(dev, B43_MMIO_DMA2_REASON) @@ -3130,7 +3123,7 @@ static int b43_chip_init(struct b43_wldev *dev) b43_write32(dev, 0x018C, 0x02000000); } b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, 0x00004000); - b43_write32(dev, B43_MMIO_DMA0_IRQ_MASK, 0x0001DC00); + b43_write32(dev, B43_MMIO_DMA0_IRQ_MASK, 0x0001FC00); b43_write32(dev, B43_MMIO_DMA1_IRQ_MASK, 0x0000DC00); b43_write32(dev, B43_MMIO_DMA2_IRQ_MASK, 0x0000DC00); b43_write32(dev, B43_MMIO_DMA3_IRQ_MASK, 0x0001DC00); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index b8f82e688c72..9a95045c97b6 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5741,8 +5741,7 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; if (il->cfg->sku & IL_SKU_N) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 48545ab00311..de2c9514bef6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -76,13 +76,16 @@ #define IWL_INVALID_STATION 255 /* device operations */ -extern struct iwl_lib_ops iwl1000_lib; -extern struct iwl_lib_ops iwl2000_lib; -extern struct iwl_lib_ops iwl2030_lib; -extern struct iwl_lib_ops iwl5000_lib; -extern struct iwl_lib_ops iwl5150_lib; -extern struct iwl_lib_ops iwl6000_lib; -extern struct iwl_lib_ops iwl6030_lib; +extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_105_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; #define TIME_UNIT 1024 @@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) { - return priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist; + return priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist; } #ifdef CONFIG_IWLWIFI_DEBUG diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index d6c4cf2ad7c5..1b0f0d502568 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); - if (priv->cfg->base_params->hd_v2) { + if (priv->lib->hd_v2) { cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = @@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv, continue; } - delta_g = (priv->cfg->base_params->chain_noise_scale * + delta_g = (priv->lib->chain_noise_scale * ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; @@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) return; /* Analyze signal for disconnected antenna */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* Disable disconnected antenna algorithm for advanced bt coex, assuming valid antennas are connected */ data->active_chains = priv->nvm_data->valid_rx_ant; diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 95ca026ecc9d..19f3ce7646ab 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -838,10 +838,6 @@ struct iwl_qosparam_cmd { #define STA_MODIFY_DELBA_TID_MSK 0x10 #define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 -/* Receiver address (actually, Rx station's index into station table), - * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ -#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) - /* agn */ struct iwl_keyinfo { __le16 key_flags; diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 71ea77576d22..f1b8df16dbba 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -568,16 +568,61 @@ struct iwl_hw_params { const struct iwl_sensitivity_ranges *sens; }; -struct iwl_lib_ops { - /* set hw dependent parameters */ +/** + * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters + * @advanced_bt_coexist: support advanced bt coexist + * @bt_init_traffic_load: specify initial bt traffic load + * @bt_prio_boost: default bt priority boost value + * @agg_time_limit: maximum number of uSec in aggregation + * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode + */ +struct iwl_dvm_bt_params { + bool advanced_bt_coexist; + u8 bt_init_traffic_load; + u32 bt_prio_boost; + u16 agg_time_limit; + bool bt_sco_disable; + bool bt_session_2; +}; + +/** + * struct iwl_dvm_cfg - DVM firmware specific device configuration + * @set_hw_params: set hardware parameters + * @set_channel_switch: send channel switch command + * @nic_config: apply device specific configuration + * @temperature: read temperature + * @adv_thermal_throttle: support advance thermal throttle + * @support_ct_kill_exit: support ct kill exit condition + * @plcp_delta_threshold: plcp error rate threshold used to trigger + * radio tuning when there is a high receiving plcp error rate + * @chain_noise_scale: default chain noise scale used for gain computation + * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up + * @no_idle_support: do not support idle mode + * @bt_params: pointer to BT parameters + * @need_temp_offset_calib: need to perform temperature offset calibration + * @no_xtal_calib: some devices do not need crystal calibration data, + * don't send it to those + * @temp_offset_v2: support v2 of temperature offset calibration + * @adv_pm: advanced power management + */ +struct iwl_dvm_cfg { void (*set_hw_params)(struct iwl_priv *priv); int (*set_channel_switch)(struct iwl_priv *priv, struct ieee80211_channel_switch *ch_switch); - /* device specific configuration */ void (*nic_config)(struct iwl_priv *priv); - - /* temperature */ void (*temperature)(struct iwl_priv *priv); + + const struct iwl_dvm_bt_params *bt_params; + s32 chain_noise_scale; + u8 plcp_delta_threshold; + bool adv_thermal_throttle; + bool support_ct_kill_exit; + bool hd_v2; + bool no_idle_support; + bool need_temp_offset_calib; + bool no_xtal_calib; + bool temp_offset_v2; + bool adv_pm; }; struct iwl_wipan_noa_data { @@ -610,7 +655,7 @@ struct iwl_priv { struct device *dev; /* for debug prints only */ const struct iwl_cfg *cfg; const struct iwl_fw *fw; - const struct iwl_lib_ops *lib; + const struct iwl_dvm_cfg *lib; unsigned long status; spinlock_t sta_lock; diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index c48907c8ab43..352c6cb7b4f1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -174,10 +174,13 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl1000_sensitivity; } -struct iwl_lib_ops iwl1000_lib = { +const struct iwl_dvm_cfg iwl_dvm_1000_cfg = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, .temperature = iwlagn_temperature, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; @@ -232,16 +235,56 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl2000_sensitivity; } -struct iwl_lib_ops iwl2000_lib = { +const struct iwl_dvm_cfg iwl_dvm_2000_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, }; -struct iwl_lib_ops iwl2030_lib = { +const struct iwl_dvm_cfg iwl_dvm_105_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, +}; + +static const struct iwl_dvm_bt_params iwl2030_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, + .bt_sco_disable = true, + .bt_session_2 = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_2030_cfg = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .bt_params = &iwl2030_bt_params, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, }; /* @@ -420,16 +463,23 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, return iwl_dvm_send_cmd(priv, &hcmd); } -struct iwl_lib_ops iwl5000_lib = { +const struct iwl_dvm_cfg iwl_dvm_5000_cfg = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwlagn_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, }; -struct iwl_lib_ops iwl5150_lib = { +const struct iwl_dvm_cfg iwl_dvm_5150_cfg = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwl5150_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, + .no_xtal_calib = true, }; @@ -584,16 +634,59 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, return err; } -struct iwl_lib_ops iwl6000_lib = { +const struct iwl_dvm_cfg iwl_dvm_6000_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, +}; + +const struct iwl_dvm_cfg iwl_dvm_6005_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .need_temp_offset_calib = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_6050_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, +}; + +static const struct iwl_dvm_bt_params iwl6000_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, + .bt_sco_disable = true, }; -struct iwl_lib_ops iwl6030_lib = { +const struct iwl_dvm_cfg iwl_dvm_6030_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .bt_params = &iwl6000_bt_params, + .need_temp_offset_calib = true, + .adv_pm = true, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 54f553380aa8..9879550a0fea 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != sizeof(basic.bt3_lookup_table)); - if (priv->cfg->bt_params) { + if (priv->lib->bt_params) { /* * newer generation of devices (2000 series and newer) * use the version 2 of the bt command * we need to make sure sending the host command * with correct data structure to avoid uCode assert */ - if (priv->cfg->bt_params->bt_session_2) { + if (priv->lib->bt_params->bt_session_2) { bt_cmd_v2.prio_boost = cpu_to_le32( - priv->cfg->bt_params->bt_prio_boost); + priv->lib->bt_params->bt_prio_boost); bt_cmd_v2.tx_prio_boost = 0; bt_cmd_v2.rx_prio_boost = 0; } else { /* older version only has 8 bits */ - WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF); + WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); bt_cmd_v1.prio_boost = - priv->cfg->bt_params->bt_prio_boost; + priv->lib->bt_params->bt_prio_boost; bt_cmd_v1.tx_prio_boost = 0; bt_cmd_v1.rx_prio_boost = 0; } @@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - if (priv->cfg->bt_params->bt_session_2) { + if (priv->lib->bt_params->bt_session_2) { memcpy(&bt_cmd_v2.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, @@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv) */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* @@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) else active_chains = priv->nvm_data->valid_rx_ant; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index cab23af0be9e..ac7ed3f809a0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -426,6 +426,10 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, if (ret) goto error; + /* let the ucode operate on its own */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + iwl_trans_d3_suspend(priv->trans); goto out; @@ -509,6 +513,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) goto out_unlock; } + /* uCode is no longer operating by itself */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + base = priv->device_pointers.error_event_table; if (!iwlagn_hw_valid_rtc_data_addr(base)) { IWL_WARN(priv, "Invalid error table during resume!\n"); @@ -1276,8 +1284,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { if (rssi_event == RSSI_EVENT_LOW) priv->bt_enable_pspoll = true; else if (rssi_event == RSSI_EVENT_HIGH) @@ -1387,7 +1395,7 @@ static int iwl_setup_interface(struct iwl_priv *priv, return err; } - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist && vif->type == NL80211_IFTYPE_ADHOC) { /* * pretend to have high BT traffic as long as we diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 74d7572e7091..68f754659570 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) priv->thermal_throttle.ct_kill_toggle = false; - if (priv->cfg->base_params->support_ct_kill_exit) { + if (priv->lib->support_ct_kill_exit) { adv_cmd.critical_temperature_enter = cpu_to_le32(priv->hw_params.ct_kill_threshold); adv_cmd.critical_temperature_exit = @@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv) } /* download priority table before any calibration request */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* Configure Bluetooth device coexistence support */ - if (priv->cfg->bt_params->bt_sco_disable) + if (priv->lib->bt_params->bt_sco_disable) priv->bt_enable_pspoll = false; else priv->bt_enable_pspoll = true; @@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv) priv->bt_status = 0; priv->cur_rssi_ctx = NULL; priv->bt_is_sco = 0; - if (priv->cfg->bt_params) + if (priv->lib->bt_params) priv->bt_traffic_load = - priv->cfg->bt_params->bt_init_traffic_load; + priv->lib->bt_params->bt_init_traffic_load; else priv->bt_traffic_load = 0; priv->bt_full_concurrent = false; @@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) iwl_setup_scan_deferred_work(priv); - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_setup_deferred_work(priv); init_timer(&priv->statistics_periodic); @@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) void iwl_cancel_deferred_work(struct iwl_priv *priv) { - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_cancel_deferred_work(priv); cancel_work_sync(&priv->run_time_calib_work); @@ -1098,8 +1098,7 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->band = IEEE80211_BAND_2GHZ; - priv->plcp_delta_threshold = - priv->cfg->base_params->plcp_delta_threshold; + priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold; priv->iw_mode = NL80211_IFTYPE_STATION; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; @@ -1116,8 +1115,8 @@ static int iwl_init_drv(struct iwl_priv *priv) iwl_init_scan_params(priv); /* init bt coex */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; @@ -1264,31 +1263,37 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, switch (priv->cfg->device_family) { case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_100: - priv->lib = &iwl1000_lib; + priv->lib = &iwl_dvm_1000_cfg; break; case IWL_DEVICE_FAMILY_2000: + priv->lib = &iwl_dvm_2000_cfg; + break; case IWL_DEVICE_FAMILY_105: - priv->lib = &iwl2000_lib; + priv->lib = &iwl_dvm_105_cfg; break; case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_135: - priv->lib = &iwl2030_lib; + priv->lib = &iwl_dvm_2030_cfg; break; case IWL_DEVICE_FAMILY_5000: - priv->lib = &iwl5000_lib; + priv->lib = &iwl_dvm_5000_cfg; break; case IWL_DEVICE_FAMILY_5150: - priv->lib = &iwl5150_lib; + priv->lib = &iwl_dvm_5150_cfg; break; case IWL_DEVICE_FAMILY_6000: - case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6000i: + priv->lib = &iwl_dvm_6000_cfg; + break; + case IWL_DEVICE_FAMILY_6005: + priv->lib = &iwl_dvm_6005_cfg; + break; case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6150: - priv->lib = &iwl6000_lib; + priv->lib = &iwl_dvm_6050_cfg; break; case IWL_DEVICE_FAMILY_6030: - priv->lib = &iwl6030_lib; + priv->lib = &iwl_dvm_6030_cfg; break; default: break; diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index bd69018d07a9..77cb59712235 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, u8 skip; u32 slp_itrvl; - if (priv->cfg->adv_pm) { + if (priv->lib->adv_pm) { table = apm_range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = apm_range_1; @@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { - if (!priv->cfg->bt_params->bt_sco_disable) + if (!priv->lib->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; @@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, if (priv->wowlan) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); - else if (!priv->cfg->base_params->no_idle_support && + else if (!priv->lib->no_idle_support && priv->hw->conf.flags & IEEE80211_CONF_IDLE) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); else if (iwl_tt_is_low_power_state(priv)) { diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 907bd6e50aad..94314a8e1029 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -1088,7 +1088,7 @@ done: (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) rs_program_fix_rate(priv, lq_sta); #endif - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) rs_bt_update_lq(priv, ctx, lq_sta); } @@ -3064,11 +3064,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, * overwrite if needed, pass aggregation time limit * to uCode in uSec */ - if (priv && priv->cfg->bt_params && - priv->cfg->bt_params->agg_time_limit && + if (priv && priv->lib->bt_params && + priv->lib->bt_params->agg_time_limit && priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(priv->cfg->bt_params->agg_time_limit); + cpu_to_le16(priv->lib->bt_params->agg_time_limit); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index a4eed2055fdb..2f3fd160ab44 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -1102,7 +1102,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) iwl_notification_wait_init(&priv->notif_wait); /* Set up BT Rx handlers */ - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_rx_handler_setup(priv); } diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index d69b55866714..8c686a5b90ac 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) * Internal scans are passive, so we can indiscriminately set * the BT ignore flag on 2.4 GHz since it applies to TX only. */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; break; case IEEE80211_BAND_5GHZ: @@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) band = priv->scan_band; if (band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* transmit 2.4 GHz probes only on first antenna */ scan_tx_antennas = first_antenna(scan_tx_antennas); } @@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) rx_ant = first_antenna(active_chains); } - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ rx_ant = first_antenna(rx_ant); diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index db183b44e038..c3c13ce96eb0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -735,7 +735,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) memcpy(&lq, priv->stations[i].lq, sizeof(struct iwl_link_quality_cmd)); - if (!memcmp(&lq, &zero_lq, sizeof(lq))) + if (memcmp(&lq, &zero_lq, sizeof(lq))) send_lq = true; } spin_unlock_bh(&priv->sta_lock); diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index 03f9bc01c0cc..fbeee081ee2f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv) INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - if (priv->cfg->base_params->adv_thermal_throttle) { + if (priv->lib->adv_thermal_throttle) { IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); tt->restriction = kcalloc(IWL_TI_STATE_MAX, sizeof(struct iwl_tt_restriction), diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index a900aaf47790..353a053b4eb1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; else if (info->band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || skb->protocol == cpu_to_be16(ETH_P_PAE))) @@ -202,8 +202,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, @@ -986,8 +986,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, * notification again. */ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); } diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 0a1cdc5e856b..86270b69cd02 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv) { int ret; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* * Tell uCode we are ready to perform calibration * need to perform this before any calibration @@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv) * temperature offset calibration is only needed for runtime ucode, * so prepare the value now. */ - if (priv->cfg->need_temp_offset_calib) { - if (priv->cfg->temp_offset_v2) + if (priv->lib->need_temp_offset_calib) { + if (priv->lib->temp_offset_v2) return iwl_set_temperature_offset_calib_v2(priv); else return iwl_set_temperature_offset_calib(priv); @@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) if (ret) return ret; - if (!priv->cfg->no_xtal_calib) { + if (!priv->lib->no_xtal_calib) { ret = iwl_set_Xtal_calib(priv); if (ret) return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index c080ae3070b2..0d2afe098afc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .led_compensation = 51, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 128, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index a6ddd2f9fba0..c727ec7c90a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, }; @@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, }; static const struct iwl_ht_params iwl2000_ht_params = { @@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; -static const struct iwl_bt_params iwl2030_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, - .bt_sco_disable = true, - .bt_session_2 = true, -}; - static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .device_family = IWL_DEVICE_FAMILY_2000, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2000_2bgn_cfg = { @@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", @@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl105_bgn_cfg = { @@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl135_bgn_cfg = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 403f3f224bf6..ecc01e1a61a1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = { .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .led_compensation = 51, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 512, - .no_idle_support = true, }; static const struct iwl_ht_params iwl5000_ht_params = { @@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = { .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ - .no_xtal_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index b5ab8d1bcac0..30d45e2fc193 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1500, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 1024, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; -static const struct iwl_bt_params iwl6000_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, - .bt_sco_disable = true, -}; - static const struct iwl_eeprom_params iwl6000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6005_2agn_cfg = { @@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", @@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 50263e87fe15..d4f3b4864ab1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -96,13 +96,9 @@ static const struct iwl_base_params iwl7000_base_params = { .pll_cfg_val = 0, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .shadow_reg_enable = true, }; static const struct iwl_ht_params iwl7000_ht_params = { @@ -118,14 +114,11 @@ static const struct iwl_ht_params iwl7000_ht_params = { .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl7000_base_params, \ - /* TODO: .bt_params? */ \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl7260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC7260", + .name = "Intel(R) Dual Band Wireless AC 7260", .fw_name_pre = IWL7260_FW_PRE, IWL_DEVICE_7000, .ht_params = &iwl7000_ht_params, @@ -133,8 +126,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, }; -const struct iwl_cfg iwl3160_ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC3160", +const struct iwl_cfg iwl7260_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl7260_n_cfg = { + .name = "Intel(R) Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_n_cfg = { + .name = "Intel(R) Wireless N 3160", .fw_name_pre = IWL3160_FW_PRE, IWL_DEVICE_7000, .ht_params = &iwl7000_ht_params, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index c38aa8f77554..a193832fc790 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -136,17 +136,9 @@ enum iwl_led_mode { * @led_compensation: compensate on the led on/off time per HW according * to the deviation to achieve the desired led frequency. * The detail algorithm is described in iwl-led.c - * @chain_noise_num_beacons: number of beacons used to compute chain noise - * @adv_thermal_throttle: support advance thermal throttle - * @support_ct_kill_exit: support ct kill exit condition - * @plcp_delta_threshold: plcp error rate threshold used to trigger - * radio tuning when there is a high receiving plcp error rate - * @chain_noise_scale: default chain noise scale used for gain computation * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadow register support - * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up - * @no_idle_support: do not support idle mode */ struct iwl_base_params { int eeprom_size; @@ -157,31 +149,9 @@ struct iwl_base_params { const u16 max_ll_items; const bool shadow_ram_support; u16 led_compensation; - bool adv_thermal_throttle; - bool support_ct_kill_exit; - u8 plcp_delta_threshold; - s32 chain_noise_scale; unsigned int wd_timeout; u32 max_event_log_size; const bool shadow_reg_enable; - const bool hd_v2; - const bool no_idle_support; -}; - -/* - * @advanced_bt_coexist: support advanced bt coexist - * @bt_init_traffic_load: specify initial bt traffic load - * @bt_prio_boost: default bt priority boost value - * @agg_time_limit: maximum number of uSec in aggregation - * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode - */ -struct iwl_bt_params { - bool advanced_bt_coexist; - u8 bt_init_traffic_load; - u32 bt_prio_boost; - u16 agg_time_limit; - bool bt_sco_disable; - bool bt_session_2; }; /* @@ -231,16 +201,10 @@ struct iwl_eeprom_params { * @nvm_calib_ver: NVM calibration version * @lib: pointer to the lib ops * @base_params: pointer to basic parameters - * @ht_params: point to ht patameters - * @bt_params: pointer to bt parameters - * @need_temp_offset_calib: need to perform temperature offset calibration - * @no_xtal_calib: some devices do not need crystal calibration data, - * don't send it to those + * @ht_params: point to ht parameters * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) - * @adv_pm: advance power management * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device - * @temp_offset_v2: support v2 of temperature offset calibration * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -264,15 +228,10 @@ struct iwl_cfg { const struct iwl_base_params *base_params; /* params likely to change within a device family */ const struct iwl_ht_params *ht_params; - const struct iwl_bt_params *bt_params; const struct iwl_eeprom_params *eeprom_params; - const bool need_temp_offset_calib; /* if used set to true */ - const bool no_xtal_calib; enum iwl_led_mode led_mode; - const bool adv_pm; const bool rx_with_siso_diversity; const bool internal_wimax_coex; - const bool temp_offset_v2; }; /* @@ -320,6 +279,10 @@ extern const struct iwl_cfg iwl105_bgn_cfg; extern const struct iwl_cfg iwl105_bgn_d_cfg; extern const struct iwl_cfg iwl135_bgn_cfg; extern const struct iwl_cfg iwl7260_2ac_cfg; -extern const struct iwl_cfg iwl3160_ac_cfg; +extern const struct iwl_cfg iwl7260_2n_cfg; +extern const struct iwl_cfg iwl7260_n_cfg; +extern const struct iwl_cfg iwl3160_2ac_cfg; +extern const struct iwl_cfg iwl3160_2n_cfg; +extern const struct iwl_cfg iwl3160_n_cfg; #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 20e845d4da04..a276af476e2d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -472,4 +472,23 @@ #define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) #define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +/* Diode Results Register Structure: */ +enum dtd_diode_reg { + DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */ + DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */ + DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */ + DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */ + DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */ + DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */ +/* Those are the masks INSIDE the flags bit-field: */ + DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0, + DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */ + DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7, + DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ +}; + #endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 39aad9893e0b..4f886133639a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1234,6 +1234,9 @@ MODULE_PARM_DESC(wd_disable, "Disable stuck queue watchdog timer 0=system default, " "1=disable, 2=enable (default: 0)"); +module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); +MODULE_PARM_DESC(nvm_file, "NVM file name"); + /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index 600c9fdd7f71..4c887f365908 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -732,17 +732,16 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data, void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band) + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains) { int max_bit_rate = 0; - u8 rx_chains; - u8 tx_chains; - tx_chains = hweight8(data->valid_tx_ant); + tx_chains = hweight8(tx_chains); if (cfg->rx_with_siso_diversity) rx_chains = 1; else - rx_chains = hweight8(data->valid_rx_ant); + rx_chains = hweight8(rx_chains); if (!(data->sku_cap_11n_enable) || !cfg->ht_params) { ht_info->ht_supported = false; @@ -806,7 +805,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + data->valid_tx_ant, data->valid_rx_ant); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; @@ -814,7 +814,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + data->valid_tx_ant, data->valid_rx_ant); if (n_channels != n_used) IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index 37f115390b19..d73304a23ec2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -133,6 +133,7 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data, void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band); + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains); #endif /* __iwl_eeprom_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index c4c446d41eb0..f844d5c748c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -106,11 +106,14 @@ enum iwl_ucode_type { /* * enumeration of ucode section. - * This enumeration is used for legacy tlv style (before 16.0 uCode). + * This enumeration is used directly for older firmware (before 16.0). + * For new firmware, there can be up to 4 sections (see below) but the + * first one packaged into the firmware file is the DATA section and + * some debugging code accesses that. */ enum iwl_ucode_sec { - IWL_UCODE_SECTION_INST, IWL_UCODE_SECTION_DATA, + IWL_UCODE_SECTION_INST, }; /* * For 16.0 uCode and above, there is no differentiation between sections, diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d6f6c37c09fd..36dfe0919f6b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -119,6 +119,7 @@ struct iwl_mod_params { int ant_coupling; bool bt_ch_announce; bool auto_agg; + char *nvm_file; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 6199a0a597a6..acd2665afb8c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -89,6 +89,7 @@ enum nvm_sku_bits { NVM_SKU_CAP_BAND_24GHZ = BIT(0), NVM_SKU_CAP_BAND_52GHZ = BIT(1), NVM_SKU_CAP_11N_ENABLE = BIT(2), + NVM_SKU_CAP_11AC_ENABLE = BIT(3), }; /* radio config bits (actual values from NVM definition) */ @@ -258,8 +259,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_vht_cap *vht_cap) { - /* For now, assume new devices with NVM are VHT capable */ - vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | @@ -292,7 +291,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_sw) + struct iwl_nvm_data *data, const __le16 *nvm_sw, + bool enable_vht, u8 tx_chains, u8 rx_chains) { int n_channels = iwl_init_channel_map(dev, cfg, data, &nvm_sw[NVM_CHANNELS]); @@ -305,7 +305,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + tx_chains, rx_chains); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; @@ -313,8 +314,10 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); - iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + tx_chains, rx_chains); + if (enable_vht) + iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); if (n_channels != n_used) IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", @@ -324,7 +327,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib) + const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains) { struct iwl_nvm_data *data; u8 hw_addr[ETH_ALEN]; @@ -380,7 +383,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - iwl_init_sbands(dev, cfg, data, nvm_sw); + iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE, + tx_chains, rx_chains); data->calib_version = 255; /* TODO: this value will prevent some checks from diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index e57fb989661e..3325059c52d4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -75,6 +75,6 @@ struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib); + const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains); #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 386f2a7c87cb..ff8cc75c189d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -100,6 +100,18 @@ /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +#define SHR_MISC_WFM_DTS_EN (0x00a10024) +#define DTSC_CFG_MODE (0x00a10604) +#define DTSC_VREF_AVG (0x00a10648) +#define DTSC_VREF5_AVG (0x00a1064c) +#define DTSC_CFG_MODE_PERIODIC (0x2) +#define DTSC_PTAT_AVG (0x00a10650) + + /** * Tx Scheduler * diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 7a13790b5bfe..84f1c8dc9741 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -189,7 +189,8 @@ enum CMD_MODE { CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), - CMD_ON_DEMAND = BIT(2), + CMD_SEND_IN_RFKILL = BIT(2), + CMD_ON_DEMAND = BIT(3), }; #define DEF_CMD_PAYLOAD_SIZE 320 @@ -455,7 +456,7 @@ struct iwl_trans_ops { int (*read_mem)(struct iwl_trans *trans, u32 addr, void *buf, int dwords); int (*write_mem)(struct iwl_trans *trans, u32 addr, - void *buf, int dwords); + const void *buf, int dwords); void (*configure)(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg); void (*set_pmi)(struct iwl_trans *trans, bool state); @@ -761,7 +762,7 @@ static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr) } static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) + const void *buf, int dwords) { return trans->ops->write_mem(trans, addr, buf, dwords); } diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 2acc44b40986..ff856e543ae8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o bt-coex.o -iwlmvm-y += led.o +iwlmvm-y += led.o tt.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index 810bfa5f6de0..f03655f303aa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -351,6 +351,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, enum ieee80211_band band; int ave_rssi; + lockdep_assert_held(&mvm->mutex); if (vif->type != NL80211_IFTYPE_STATION) return; @@ -365,7 +366,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = IEEE80211_SMPS_AUTOMATIC; if (band != IEEE80211_BAND_2GHZ) { - ieee80211_request_smps(vif, smps_mode); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); return; } @@ -380,7 +382,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, mvmvif->id, data->notif->bt_status, data->notif->bt_traffic_load, smps_mode); - ieee80211_request_smps(vif, smps_mode); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); /* don't reduce the Tx power if in loose scheme */ if (is_loose_coex()) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 16bbdcc8627a..4d3c978b5c76 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1007,6 +1007,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto out; + ret = iwl_mvm_power_update_mode(mvm, vif); + if (ret) + goto out; + /* must be last -- this switches firmware state */ ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, sizeof(d3_cfg_cmd), &d3_cfg_cmd); @@ -1214,6 +1218,26 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, iwl_free_resp(&cmd); } +static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) +{ +#ifdef CONFIG_IWLWIFI_DEBUGFS + const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + u32 len = img->sec[IWL_UCODE_SECTION_DATA].len; + u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset; + + if (!mvm->store_d3_resume_sram) + return; + + if (!mvm->d3_resume_sram) { + mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL); + if (!mvm->d3_resume_sram) + return; + } + + iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len); +#endif +} + int iwl_mvm_resume(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1245,6 +1269,9 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) goto out_unlock; } + /* query SRAM first in case we want event logging */ + iwl_mvm_read_d3_sram(mvm); + iwl_mvm_query_wakeup_reasons(mvm, vif); out_unlock: diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 2053dccefcd6..69e0806075a2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -145,15 +145,18 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, char *buf; u8 *ptr; + if (!mvm->ucode_loaded) + return -EINVAL; + /* default is to dump the entire data segment */ if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { - mvm->dbgfs_sram_offset = 0x800000; - if (!mvm->ucode_loaded) - return -EINVAL; img = &mvm->fw->img[mvm->cur_ucode]; - mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + ofs = mvm->dbgfs_sram_offset; + len = mvm->dbgfs_sram_len; } - len = mvm->dbgfs_sram_len; bufsz = len * 4 + 256; buf = kzalloc(bufsz, GFP_KERNEL); @@ -167,12 +170,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, } pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len); - pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - mvm->dbgfs_sram_offset); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs); - iwl_trans_read_mem_bytes(mvm->trans, - mvm->dbgfs_sram_offset, - ptr, len); + iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); for (ofs = 0; ofs < len; ofs += 16) { pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, @@ -300,6 +300,146 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file, return count; } +static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_dbgfs_pm_mask param, int val) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; + + dbgfs_pm->mask |= param; + + switch (param) { + case MVM_DEBUGFS_PM_KEEP_ALIVE: { + struct ieee80211_hw *hw = mvm->hw; + int dtimper = hw->conf.ps_dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); + if (val * MSEC_PER_SEC < 3 * dtimper_msec) { + IWL_WARN(mvm, + "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", + val * MSEC_PER_SEC, 3 * dtimper_msec); + } + dbgfs_pm->keep_alive_seconds = val; + break; + } + case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: + IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", + val ? "enabled" : "disabled"); + dbgfs_pm->skip_over_dtim = val; + break; + case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: + IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); + dbgfs_pm->skip_dtim_periods = val; + break; + case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); + dbgfs_pm->rx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); + dbgfs_pm->tx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: + IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); + dbgfs_pm->disable_power_off = val; + break; + } +} + +static ssize_t iwl_dbgfs_pm_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + enum iwl_dbgfs_pm_mask param; + char buf[32] = {}; + int val; + int ret; + + if (copy_from_user(buf, user_buf, sizeof(buf))) + return -EFAULT; + + if (!strncmp("keep_alive=", buf, 11)) { + if (sscanf(buf + 11, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_KEEP_ALIVE; + } else if (!strncmp("skip_over_dtim=", buf, 15)) { + if (sscanf(buf + 15, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; + } else if (!strncmp("skip_dtim_periods=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; + } else if (!strncmp("rx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; + } else if (!strncmp("tx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; + } else if (!strncmp("disable_power_off=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_pm(mvm, vif, param, val); + ret = iwl_mvm_power_update_mode(mvm, vif); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_pm_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + struct iwl_powertable_cmd cmd = {}; + char buf[256]; + int bufsz = sizeof(buf); + int pos = 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? + 0 : 1); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + le32_to_cpu(cmd.skip_dtim_periods)); + pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", + iwlmvm_mod_params.power_scheme); + pos += scnprintf(buf+pos, bufsz-pos, "flags = %d\n", + le16_to_cpu(cmd.flags)); + pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", + cmd.keep_alive_seconds); + + if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? + 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + static ssize_t iwl_dbgfs_mac_params_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -481,6 +621,255 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, return count; } +static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, + enum iwl_dbgfs_bf_mask param, int value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + dbgfs_bf->mask |= param; + + switch (param) { + case MVM_DEBUGFS_BF_ENERGY_DELTA: + dbgfs_bf->bf_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: + dbgfs_bf->bf_roaming_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_STATE: + dbgfs_bf->bf_roaming_state = value; + break; + case MVM_DEBUGFS_BF_TEMPERATURE_DELTA: + dbgfs_bf->bf_temperature_delta = value; + break; + case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: + dbgfs_bf->bf_enable_beacon_filter = value; + break; + case MVM_DEBUGFS_BF_DEBUG_FLAG: + dbgfs_bf->bf_debug_flag = value; + break; + case MVM_DEBUGFS_BF_ESCAPE_TIMER: + dbgfs_bf->bf_escape_timer = value; + break; + case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: + dbgfs_bf->ba_enable_beacon_abort = value; + break; + case MVM_DEBUGFS_BA_ESCAPE_TIMER: + dbgfs_bf->ba_escape_timer = value; + break; + } +} + +static ssize_t iwl_dbgfs_bf_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + enum iwl_dbgfs_bf_mask param; + char buf[256]; + int buf_size; + int value; + int ret = 0; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (!strncmp("bf_energy_delta=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ENERGY_DELTA_MIN || + value > IWL_BF_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || + value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_state=", buf, 17)) { + if (sscanf(buf+17, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_STATE_MIN || + value > IWL_BF_ROAMING_STATE_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_STATE; + } else if (!strncmp("bf_temperature_delta=", buf, 21)) { + if (sscanf(buf+21, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMPERATURE_DELTA_MIN || + value > IWL_BF_TEMPERATURE_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA; + } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; + } else if (!strncmp("bf_debug_flag=", buf, 14)) { + if (sscanf(buf+14, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_DEBUG_FLAG; + } else if (!strncmp("bf_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ESCAPE_TIMER_MIN || + value > IWL_BF_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ESCAPE_TIMER; + } else if (!strncmp("ba_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BA_ESCAPE_TIMER_MIN || + value > IWL_BA_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BA_ESCAPE_TIMER; + } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { + if (sscanf(buf+23, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_bf(vif, param, value); + if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) { + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + } else { + if (mvmvif->bf_enabled) + ret = iwl_mvm_enable_beacon_filter(mvm, vif); + else + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + } + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_bf_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_beacon_filter_cmd cmd = { + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, + .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT, + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT), + .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT, + }; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + if (mvmvif->bf_enabled) + cmd.bf_enable_beacon_filter = 1; + else + cmd.bf_enable_beacon_filter = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", + cmd.bf_energy_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", + cmd.bf_roaming_energy_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", + cmd.bf_roaming_state); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n", + cmd.bf_temperature_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", + cmd.bf_enable_beacon_filter); + pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", + cmd.bf_debug_flag); + pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", + cmd.bf_escape_timer); + pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", + cmd.ba_escape_timer); + pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", + cmd.ba_enable_beacon_abort); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +#ifdef CONFIG_PM_SLEEP +static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[8] = {}; + int store; + + if (copy_from_user(buf, user_buf, sizeof(buf))) + return -EFAULT; + + if (sscanf(buf, "%d", &store) != 1) + return -EINVAL; + + mvm->store_d3_resume_sram = store; + + return count; +} + +static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + const struct fw_img *img; + int ofs, len, pos = 0; + size_t bufsz, ret; + char *buf; + u8 *ptr = mvm->d3_resume_sram; + + img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + bufsz = len * 4 + 256; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n", + mvm->store_d3_resume_sram ? "en" : "dis"); + + if (ptr) { + for (ofs = 0; ofs < len; ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, + bufsz - pos, false); + pos += strlen(buf + pos); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "(no data captured)\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + + kfree(buf); + + return ret; +} +#endif + #define MVM_DEBUGFS_READ_FILE_OPS(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ @@ -524,9 +913,14 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); +#ifdef CONFIG_PM_SLEEP +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); +#endif /* Interface specific debugfs entries */ MVM_DEBUGFS_READ_FILE_OPS(mac_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params); int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) { @@ -542,6 +936,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); +#ifdef CONFIG_PM_SLEEP + MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); +#endif /* * Create a symlink with mac80211. It will be removed when mac80211 @@ -577,9 +974,19 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return; } + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p) + MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | + S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvmvif == mvm->bf_allowed_vif) + MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + /* * Create symlink for convenience pointing to interface specific * debugfs entries for the driver. For example, under diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 51e015d1dfb2..6f8b2c16ae17 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -75,13 +75,15 @@ enum iwl_d3_wakeup_flags { * struct iwl_d3_manager_config - D3 manager configuration command * @min_sleep_time: minimum sleep time (in usec) * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags + * @wakeup_host_timer: force wakeup after this many seconds * * The structure is used for the D3_CONFIG_CMD command. */ struct iwl_d3_manager_config { __le32 min_sleep_time; __le32 wakeup_flags; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */ + __le32 wakeup_host_timer; +} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ /* TODO: OFFLOADS_QUERY_API_S_VER_1 */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 81fe45f46be7..d8e19290b0f3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -101,20 +101,107 @@ enum iwl_power_flags { * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to * PSM transition - legacy PM * @sleep_interval: not in use - * @keep_alive_beacons: not in use + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. * Default: 80dbm */ struct iwl_powertable_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_5 */ + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ __le16 flags; u8 keep_alive_seconds; u8 debug_flags; __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 keep_alive_beacons; + __le32 skip_dtim_periods; __le32 lprx_rssi_threshold; } __packed; +/** + * struct iwl_beacon_filter_cmd + * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) + * @id_and_color: MAC contex identifier + * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon + * to driver if delta in Energy values calculated for this and last + * passed beacon is greater than this threshold. Zero value means that + * the Energy change is ignored for beacon filtering, and beacon will + * not be forced to be sent to driver regardless of this delta. Typical + * energy delta 5dB. + * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. + * Send beacon to driver if delta in Energy values calculated for this + * and last passed beacon is greater than this threshold. Zero value + * means that the Energy change is ignored for beacon filtering while in + * Roaming state, typical energy delta 1dB. + * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values + * calculated for current beacon is less than the threshold, use + * Roaming Energy Delta Threshold, otherwise use normal Energy Delta + * Threshold. Typical energy threshold is -72dBm. + * @bf_temperature_delta: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature changeis ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. + * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed + * for a specific period of time. Units: Beacons. + * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed + * for a longer period of time then this escape-timeout. Units: Beacons. + * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. + */ +struct iwl_beacon_filter_cmd { + u8 bf_energy_delta; + u8 bf_roaming_energy_delta; + u8 bf_roaming_state; + u8 bf_temperature_delta; + u8 bf_enable_beacon_filter; + u8 bf_debug_flag; + __le16 reserved1; + __le32 bf_escape_timer; + __le32 ba_escape_timer; + u8 ba_enable_beacon_abort; + u8 reserved2[3]; +} __packed; + +/* Beacon filtering and beacon abort */ +#define IWL_BF_ENERGY_DELTA_DEFAULT 5 +#define IWL_BF_ENERGY_DELTA_MAX 255 +#define IWL_BF_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 +#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 +#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_STATE_DEFAULT 72 +#define IWL_BF_ROAMING_STATE_MAX 255 +#define IWL_BF_ROAMING_STATE_MIN 0 + +#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5 +#define IWL_BF_TEMPERATURE_DELTA_MAX 255 +#define IWL_BF_TEMPERATURE_DELTA_MIN 0 + +#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 + +#define IWL_BF_DEBUG_FLAG_DEFAULT 0 + +#define IWL_BF_ESCAPE_TIMER_DEFAULT 50 +#define IWL_BF_ESCAPE_TIMER_MAX 1024 +#define IWL_BF_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ESCAPE_TIMER_DEFAULT 3 +#define IWL_BA_ESCAPE_TIMER_MAX 1024 +#define IWL_BA_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 + +#define IWL_BF_CMD_CONFIG_DEFAULTS \ + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, \ + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \ + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, \ + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, \ + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \ + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT) + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 007a93b25bd7..6994232f5726 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -134,6 +134,7 @@ enum iwl_tx_flags { #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x07 #define TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 #define TX_CMD_SEC_KEY128 0x08 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 191dcae8ba47..cbfb3beae783 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -139,6 +139,9 @@ enum { /* Power */ POWER_TABLE_CMD = 0x77, + /* Thermal Throttling*/ + REPLY_THERMAL_MNG_BACKOFF = 0x7e, + /* Scanning */ SCAN_REQUEST_CMD = 0x80, SCAN_ABORT_CMD = 0x81, @@ -161,6 +164,8 @@ enum { CARD_STATE_CMD = 0xa0, CARD_STATE_NOTIFICATION = 0xa1, + MISSED_BEACONS_NOTIFICATION = 0xa2, + REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, @@ -170,9 +175,13 @@ enum { BT_COEX_PROT_ENV = 0xcd, BT_PROFILE_NOTIFICATION = 0xce, + REPLY_BEACON_FILTERING_CMD = 0xd2, + REPLY_DEBUG_CMD = 0xf0, DEBUG_LOG_MSG = 0xf7, + MCAST_FILTER_CMD = 0xd0, + /* D3 commands/notifications */ D3_CONFIG_CMD = 0xd3, PROT_OFFLOAD_CONFIG_CMD = 0xd4, @@ -936,6 +945,24 @@ struct iwl_card_state_notif { } __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ /** + * struct iwl_missed_beacons_notif - information on missed beacons + * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) + * @mac_id: interface ID + * @consec_missed_beacons_since_last_rx: number of consecutive missed + * beacons since last RX. + * @consec_missed_beacons: number of consecutive missed beacons + * @num_expected_beacons: + * @num_recvd_beacons: + */ +struct iwl_missed_beacons_notif { + __le32 mac_id; + __le32 consec_missed_beacons_since_last_rx; + __le32 consec_missed_beacons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ + +/** * struct iwl_set_calib_default_cmd - set default value for calibration. * ( SET_CALIB_DEFAULT_CMD = 0x8e ) * @calib_index: the calibration to set value for @@ -948,4 +975,237 @@ struct iwl_set_calib_default_cmd { u8 data[0]; } __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */ +#define MAX_PORT_ID_NUM 2 + +/** + * struct iwl_mcast_filter_cmd - configure multicast filter. + * @filter_own: Set 1 to filter out multicast packets sent by station itself + * @port_id: Multicast MAC addresses array specifier. This is a strange way + * to identify network interface adopted in host-device IF. + * It is used by FW as index in array of addresses. This array has + * MAX_PORT_ID_NUM members. + * @count: Number of MAC addresses in the array + * @pass_all: Set 1 to pass all multicast packets. + * @bssid: current association BSSID. + * @addr_list: Place holder for array of MAC addresses. + * IMPORTANT: add padding if necessary to ensure DWORD alignment. + */ +struct iwl_mcast_filter_cmd { + u8 filter_own; + u8 port_id; + u8 count; + u8 pass_all; + u8 bssid[6]; + u8 reserved[2]; + u8 addr_list[0]; +} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ + +struct mvm_statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ + +struct mvm_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 rssi_ant; + __le32 reserved2; +} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ + +struct mvm_statistics_general_common { + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* radio voltage */ + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; + __le32 directed_data_mpdu; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ + +#define MAX_CHAINS 3 + +struct mvm_statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; + __s8 txpower[MAX_CHAINS]; + __s8 reserved; + __le32 reserved2; +} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ + +struct mvm_statistics_tx_channel_width { + __le32 ext_cca_narrow_ch20[1]; + __le32 ext_cca_narrow_ch40[2]; + __le32 ext_cca_narrow_ch80[3]; + __le32 ext_cca_narrow_ch160[4]; + __le32 last_tx_ch_width_indx; + __le32 rx_detected_per_ch_width[4]; + __le32 success_per_ch_width[4]; + __le32 fail_per_ch_width[4]; +}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ + +struct mvm_statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_4 */ + + +struct mvm_statistics_bt_activity { + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ + +struct mvm_statistics_general { + struct mvm_statistics_general_common common; + __le32 beacon_filtered; + __le32 missed_beacons; + __s8 beacon_filter_everage_energy; + __s8 beacon_filter_reason; + __s8 beacon_filter_current_energy; + __s8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx { + struct mvm_statistics_rx_phy ofdm; + struct mvm_statistics_rx_phy cck; + struct mvm_statistics_rx_non_phy general; + struct mvm_statistics_rx_ht_phy ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_3 */ + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ + +struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */ + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general general; +} __packed; + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index e18c92dd60ec..cd7c0032cc58 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -326,6 +326,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); WARN_ON(ret); + /* + * abort after reading the nvm in case RF Kill is on, we will complete + * the init seq later when RF kill will switch to off + */ + if (iwl_mvm_is_radio_killed(mvm)) { + IWL_DEBUG_RF_KILL(mvm, + "jump over all phy activities due to RF kill\n"); + iwl_remove_notification(&mvm->notif_wait, &calib_wait); + return 1; + } + /* Send TX valid antennas before triggering calibrations */ ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); if (ret) @@ -388,6 +399,8 @@ out: int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; + struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); @@ -400,8 +413,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); + /* this can't happen */ + if (WARN_ON(ret > 0)) + ret = -ERFKILL; goto error; } + /* should stop & start HW since that INIT image just loaded */ + iwl_trans_stop_hw(mvm->trans, false); + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; } if (iwlmvm_mod_params.init_dbg) @@ -443,8 +464,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; - IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); + /* Add all the PHY contexts */ + chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + for (i = 0; i < NUM_PHY_CTX; i++) { + /* + * The channel used here isn't relevant as it's + * going to be overwritten in the other flows. + * For now use the first channel we have. + */ + ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], + &chandef, 1, 1); + if (ret) + goto error; + } + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: iwl_trans_stop_device(mvm->trans); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index e6eca4d66f6c..b4e8e597d2b7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -227,7 +227,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, .found_vif = false, }; u32 ac; - int ret; + int ret, i; /* * Allocate a MAC ID and a TSF for this MAC, along with the queues @@ -335,6 +335,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) + mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; + return 0; exit_fail: @@ -586,10 +589,12 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, */ static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_data_sta *ctxt_sta) + struct iwl_mac_data_sta *ctxt_sta, + bool force_assoc_off) { /* We need the dtim_period to set the MAC as associated */ - if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) { + if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && + !force_assoc_off) { u32 dtim_offs; /* @@ -659,7 +664,8 @@ static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm, cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON); /* Fill the data specific for station mode */ - iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta); + iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta, + action == FW_CTXT_ACTION_ADD); return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -677,7 +683,8 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); /* Fill the data specific for station mode */ - iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta); + iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta, + action == FW_CTXT_ACTION_ADD); cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK); @@ -1043,3 +1050,28 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, rate); return 0; } + +static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u16 *id = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (mvmvif->id == *id) + ieee80211_beacon_loss(vif); +} + +int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data; + u16 id = (u16)le32_to_cpu(missed_beacons->mac_id); + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_beacon_loss_iterator, + &id); + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index dd158ec571fb..dc50020d746d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -127,6 +127,17 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { }; #endif +static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) +{ + int i; + + memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts)); + for (i = 0; i < NUM_PHY_CTX; i++) { + mvm->phy_ctxts[i].id = i; + mvm->phy_ctxts[i].ref = 0; + } +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -141,7 +152,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_TIMING_BEACON_ONLY; + IEEE80211_HW_TIMING_BEACON_ONLY | + IEEE80211_HW_CONNECTION_MONITOR; hw->queues = IWL_MVM_FIRST_AGG_QUEUE; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; @@ -158,7 +170,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); - hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt); + hw->chanctx_data_size = sizeof(u16); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) | @@ -193,6 +205,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->n_addresses++; } + iwl_mvm_reset_phy_ctxts(mvm); + /* we create the 802.11 header and a max-length SSID element */ hw->wiphy->max_scan_ie_len = mvm->fw->ucode_capa.max_probe_length - 24 - 34; @@ -252,8 +266,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { - IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n"); + if (iwl_mvm_is_radio_killed(mvm)) { + IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); goto drop; } @@ -345,8 +359,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); spin_unlock_bh(&mvm->time_event_lock); - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvmvif->phy_ctxt = NULL; + mvmvif->phy_ctxt = NULL; } static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) @@ -363,6 +376,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_cleanup_iterator, mvm); + mvm->p2p_device_vif = NULL; + + iwl_mvm_reset_phy_ctxts(mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); @@ -456,6 +472,20 @@ static void iwl_mvm_power_update_iterator(void *data, u8 *mac, iwl_mvm_power_update_mode(mvm, vif); } +static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) +{ + u16 i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < NUM_PHY_CTX; i++) + if (!mvm->phy_ctxts[i].ref) + return &mvm->phy_ctxts[i]; + + IWL_ERR(mvm, "No available PHY context\n"); + return NULL; +} + static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -530,32 +560,34 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ iwl_mvm_power_update_mode(mvm, vif); + /* beacon filtering */ + if (!mvm->bf_allowed_vif && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p){ + mvm->bf_allowed_vif = mvmvif; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + } + + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + if (ret) + goto out_release; + /* * P2P_DEVICE interface does not have a channel context assigned to it, * so a dedicated PHY context is allocated to it and the corresponding * MAC context is bound to it at this stage. */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - struct ieee80211_channel *chan; - struct cfg80211_chan_def chandef; - - mvmvif->phy_ctxt = &mvm->phy_ctxt_roc; - /* - * The channel used here isn't relevant as it's - * going to be overwritten as part of the ROC flow. - * For now use the first channel we have. - */ - chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, - &chandef, 1, 1); - if (ret) + mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!mvmvif->phy_ctxt) { + ret = -ENOSPC; goto out_remove_mac; + } + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) - goto out_remove_phy; + goto out_unref_phy; ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) @@ -571,8 +603,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); - out_remove_phy: - iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); + out_unref_phy: + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); out_remove_mac: mvmvif->phy_ctxt = NULL; iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -646,6 +678,11 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + } + iwl_mvm_vif_dbgfs_clean(mvm, vif); /* @@ -661,7 +698,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->p2p_device_vif = NULL; iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); mvmvif->phy_ctxt = NULL; } @@ -701,6 +738,20 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, *total_flags = 0; } +static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mcast_filter_cmd mcast_filter_cmd = { + .pass_all = 1, + }; + + memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN); + + return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, + sizeof(mcast_filter_cmd), + &mcast_filter_cmd); +} + static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -722,6 +773,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, return; } iwl_mvm_bt_coex_vif_assoc(mvm, vif); + iwl_mvm_configure_mcast_filter(mvm, vif); } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { /* remove AP station now that the MAC is unassoc */ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); @@ -931,7 +983,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, switch (cmd) { case STA_NOTIFY_SLEEP: - if (atomic_read(&mvmsta->pending_frames) > 0) + if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) ieee80211_sta_block_awake(hw, sta, true); /* * The fw updates the STA to be asleep. Tx packets on the Tx @@ -984,9 +1036,13 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, mvmvif->phy_ctxt->channel->band); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { + /* enable beacon filtering */ + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { + /* disable beacon filtering */ + WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif)); ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { @@ -1152,29 +1208,107 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, enum ieee80211_roc_type type) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct cfg80211_chan_def chandef; - int ret; + struct iwl_mvm_phy_ctxt *phy_ctxt; + int ret, i; + + IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, + duration, type); if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type); return -EINVAL; } - IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, - duration, type); - mutex_lock(&mvm->mutex); + for (i = 0; i < NUM_PHY_CTX; i++) { + phy_ctxt = &mvm->phy_ctxts[i]; + if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) + continue; + + if (phy_ctxt->ref && channel == phy_ctxt->channel) { + /* + * Unbind the P2P_DEVICE from the current PHY context, + * and if the PHY context is not used remove it. + */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the current PHY Context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + goto schedule_time_event; + } + } + + /* Need to update the PHY context only if the ROC channel changed */ + if (channel == mvmvif->phy_ctxt->channel) + goto schedule_time_event; + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc, - &chandef, 1, 1); + /* + * Change the PHY context configuration as it is currently referenced + * only by the P2P Device MAC + */ + if (mvmvif->phy_ctxt->ref == 1) { + ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, + &chandef, 1, 1); + if (ret) + goto out_unlock; + } else { + /* + * The PHY context is shared with other MACs. Need to remove the + * P2P Device from the binding, allocate an new PHY context and + * create a new binding + */ + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out_unlock; + } + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, + 1, 1); + if (ret) { + IWL_ERR(mvm, "Failed to change PHY context\n"); + goto out_unlock; + } + + /* Unbind the P2P_DEVICE from the current PHY context */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the new allocated PHY context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + } + +schedule_time_event: /* Schedule the time events */ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); +out_unlock: mutex_unlock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "leave\n"); - return ret; } @@ -1196,15 +1330,30 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt; int ret; + IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); + mutex_lock(&mvm->mutex); + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out; + } + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + ctx->rx_chains_static, + ctx->rx_chains_dynamic); + if (ret) { + IWL_ERR(mvm, "Failed to add PHY context\n"); + goto out; + } - IWL_DEBUG_MAC80211(mvm, "Add PHY context\n"); - ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); + iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); + *phy_ctxt_id = phy_ctxt->id; +out: mutex_unlock(&mvm->mutex); return ret; } @@ -1213,10 +1362,11 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); mutex_unlock(&mvm->mutex); } @@ -1225,7 +1375,16 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, u32 changed) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + + if (WARN_ONCE((phy_ctxt->ref > 1) && + (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | + IEEE80211_CHANCTX_CHANGE_RX_CHAINS | + IEEE80211_CHANCTX_CHANGE_RADAR)), + "Cannot change PHY. Ref=%d, changed=0x%X\n", + phy_ctxt->ref, changed)) + return; mutex_lock(&mvm->mutex); iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, @@ -1239,13 +1398,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; mutex_lock(&mvm->mutex); - mvmvif->phy_ctxt = phyctx; + mvmvif->phy_ctxt = phy_ctxt; switch (vif->type) { case NL80211_IFTYPE_AP: diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 8269bc562951..6a3220b6273e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -109,6 +109,7 @@ extern struct iwl_mvm_mod_params iwlmvm_mod_params; struct iwl_mvm_phy_ctxt { u16 id; u16 color; + u32 ref; /* * TODO: This should probably be removed. Currently here only for rate @@ -149,6 +150,60 @@ enum iwl_power_scheme { #define IWL_CONN_MAX_LISTEN_INTERVAL 70 +#ifdef CONFIG_IWLWIFI_DEBUGFS +enum iwl_dbgfs_pm_mask { + MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), + MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1), + MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), + MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), + MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), + MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5), +}; + +struct iwl_dbgfs_pm { + u8 keep_alive_seconds; + u32 rx_data_timeout; + u32 tx_data_timeout; + bool skip_over_dtim; + u8 skip_dtim_periods; + bool disable_power_off; + int mask; +}; + +/* beacon filtering */ + +enum iwl_dbgfs_bf_mask { + MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), + MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), + MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), + MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3), + MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4), + MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5), + MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6), + MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7), + MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8), +}; + +struct iwl_dbgfs_bf { + u8 bf_energy_delta; + u8 bf_roaming_energy_delta; + u8 bf_roaming_state; + u8 bf_temperature_delta; + u8 bf_enable_beacon_filter; + u8 bf_debug_flag; + u32 bf_escape_timer; + u32 ba_escape_timer; + u8 ba_enable_beacon_abort; + int mask; +}; +#endif + +enum iwl_mvm_smps_type_request { + IWL_MVM_SMPS_REQ_BT_COEX, + IWL_MVM_SMPS_REQ_TT, + NUM_IWL_MVM_SMPS_REQ, +}; + /** * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context * @id: between 0 and 3 @@ -163,6 +218,8 @@ enum iwl_power_scheme { * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. * @beacon_skb: the skb used to hold the AP/GO beacon template + * @smps_requests: the requests of of differents parts of the driver, regard + the desired smps mode. */ struct iwl_mvm_vif { u16 id; @@ -172,6 +229,8 @@ struct iwl_mvm_vif { bool uploaded; bool ap_active; bool monitor_active; + /* indicate whether beacon filtering is enabled */ + bool bf_enabled; u32 ap_beacon_time; @@ -214,7 +273,11 @@ struct iwl_mvm_vif { struct dentry *dbgfs_dir; struct dentry *dbgfs_slink; void *dbgfs_data; + struct iwl_dbgfs_pm dbgfs_pm; + struct iwl_dbgfs_bf dbgfs_bf; #endif + + enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; }; static inline struct iwl_mvm_vif * @@ -223,12 +286,6 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) return (void *)vif->drv_priv; } -enum iwl_mvm_status { - IWL_MVM_STATUS_HW_RFKILL, - IWL_MVM_STATUS_ROC_RUNNING, - IWL_MVM_STATUS_IN_HW_RESTART, -}; - enum iwl_scan_status { IWL_MVM_SCAN_NONE, IWL_MVM_SCAN_OS, @@ -246,6 +303,63 @@ struct iwl_nvm_section { const u8 *data; }; +/* + * Tx-backoff threshold + * @temperature: The threshold in Celsius + * @backoff: The tx-backoff in uSec + */ +struct iwl_tt_tx_backoff { + s32 temperature; + u32 backoff; +}; + +#define TT_TX_BACKOFF_SIZE 6 + +/** + * struct iwl_tt_params - thermal throttling parameters + * @ct_kill_entry: CT Kill entry threshold + * @ct_kill_exit: CT Kill exit threshold + * @ct_kill_duration: The time intervals (in uSec) in which the driver needs + * to checks whether to exit CT Kill. + * @dynamic_smps_entry: Dynamic SMPS entry threshold + * @dynamic_smps_exit: Dynamic SMPS exit threshold + * @tx_protection_entry: TX protection entry threshold + * @tx_protection_exit: TX protection exit threshold + * @tx_backoff: Array of thresholds for tx-backoff , in ascending order. + * @support_ct_kill: Support CT Kill? + * @support_dynamic_smps: Support dynamic SMPS? + * @support_tx_protection: Support tx protection? + * @support_tx_backoff: Support tx-backoff? + */ +struct iwl_tt_params { + s32 ct_kill_entry; + s32 ct_kill_exit; + u32 ct_kill_duration; + s32 dynamic_smps_entry; + s32 dynamic_smps_exit; + s32 tx_protection_entry; + s32 tx_protection_exit; + struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE]; + bool support_ct_kill; + bool support_dynamic_smps; + bool support_tx_protection; + bool support_tx_backoff; +}; + +/** + * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure + * @ct_kill_exit: worker to exit thermal kill + * @dynamic_smps: Is thermal throttling enabled dynamic_smps? + * @tx_backoff: The current thremal throttling tx backoff in uSec. + * @params: Parameters to configure the thermal throttling algorithm. + */ +struct iwl_mvm_tt_mgmt { + struct delayed_work ct_kill_exit; + bool dynamic_smps; + u32 tx_backoff; + const struct iwl_tt_params *params; +}; + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -266,6 +380,12 @@ struct iwl_mvm { unsigned long status; + /* + * for beacon filtering - + * currently only one interface can be supported + */ + struct iwl_mvm_vif *bf_allowed_vif; + enum iwl_ucode_type cur_ucode; bool ucode_loaded; bool init_ucode_run; @@ -292,6 +412,7 @@ struct iwl_mvm { struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT]; struct work_struct sta_drained_wk; unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)]; + atomic_t pending_frames[IWL_MVM_STATION_COUNT]; /* configured by mac80211 */ u32 rts_threshold; @@ -312,7 +433,7 @@ struct iwl_mvm { bool prevent_power_down_d3; #endif - struct iwl_mvm_phy_ctxt phy_ctxt_roc; + struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; struct list_head time_event_list; spinlock_t time_event_lock; @@ -337,11 +458,19 @@ struct iwl_mvm { #ifdef CONFIG_PM_SLEEP int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool store_d3_resume_sram; + void *d3_resume_sram; +#endif #endif /* BT-Coex */ u8 bt_kill_msk; struct iwl_bt_coex_profile_notif last_bt_notif; + + /* Thermal Throttling and CTkill */ + struct iwl_mvm_tt_mgmt thermal_throttle; + s32 temperature; /* Celsius */ }; /* Extract MVM priv from op_mode and _hw */ @@ -351,6 +480,19 @@ struct iwl_mvm { #define IWL_MAC80211_GET_MVM(_hw) \ IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) +enum iwl_mvm_status { + IWL_MVM_STATUS_HW_RFKILL, + IWL_MVM_STATUS_HW_CTKILL, + IWL_MVM_STATUS_ROC_RUNNING, + IWL_MVM_STATUS_IN_HW_RESTART, +}; + +static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) +{ + return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) || + test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); +} + extern const u8 iwl_mvm_ac_to_tx_fifo[]; struct iwl_rate_info { @@ -442,8 +584,10 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic); -void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); /* MAC (virtual interface) programming */ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -458,6 +602,9 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -533,4 +680,36 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +/* beacon filtering */ +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd); +int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm); +#else +static inline void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{} +static inline int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm) +{ + return 0; +} +#endif +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); + +/* SMPS */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request); + +/* Thermal management and CT-kill */ +void iwl_mvm_tt_handler(struct iwl_mvm *mvm); +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm); +void iwl_mvm_tt_exit(struct iwl_mvm *mvm); +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index b8ec02f89acc..edb94ea31654 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -60,6 +60,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +#include <linux/firmware.h> #include "iwl-trans.h" #include "mvm.h" #include "iwl-eeprom-parse.h" @@ -75,31 +76,56 @@ static const int nvm_to_read[] = { }; /* Default NVM size to read */ -#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024); +#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWL_MAX_NVM_SECTION_SIZE 6000 -static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd, - u16 offset, u16 length, u16 section) +#define NVM_WRITE_OPCODE 1 +#define NVM_READ_OPCODE 0 + +/* + * prepare the NVM host command w/ the pointers to the nvm buffer + * and send it to fw + */ +static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, + u16 offset, u16 length, const u8 *data) { - cmd->offset = cpu_to_le16(offset); - cmd->length = cpu_to_le16(length); - cmd->type = cpu_to_le16(section); + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_WRITE_OPCODE, + }; + struct iwl_host_cmd cmd = { + .id = NVM_ACCESS_CMD, + .len = { sizeof(struct iwl_nvm_access_cmd), length }, + .flags = CMD_SYNC | CMD_SEND_IN_RFKILL, + .data = { &nvm_access_cmd, data }, + /* data may come from vmalloc, so use _DUP */ + .dataflags = { 0, IWL_HCMD_DFL_DUP }, + }; + + return iwl_mvm_send_cmd(mvm, &cmd); } static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, u16 offset, u16 length, u8 *data) { - struct iwl_nvm_access_cmd nvm_access_cmd = {}; + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_READ_OPCODE, + }; struct iwl_nvm_access_resp *nvm_resp; struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; int ret, bytes_read, offset_read; u8 *resp_data; - iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section); cmd.len[0] = sizeof(struct iwl_nvm_access_cmd); ret = iwl_mvm_send_cmd(mvm, &cmd); @@ -144,6 +170,30 @@ exit: return ret; } +static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, + const u8 *data, u16 length) +{ + int offset = 0; + + /* copy data in chunks of 2k (and remainder if any) */ + + while (offset < length) { + int chunk_size, ret; + + chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE, + length - offset); + + ret = iwl_nvm_write_chunk(mvm, section, offset, + chunk_size, data + offset); + if (ret < 0) + return ret; + + offset += chunk_size; + } + + return 0; +} + /* * Reads an NVM section completely. * NICs prior to 7000 family doesn't have a real NVM, but just read @@ -177,7 +227,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, offset += ret; } - IWL_INFO(mvm, "NVM section %d read completed\n", section); + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM section %d read completed\n", section); return offset; } @@ -200,7 +251,130 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; - return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib); + return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, + iwl_fw_valid_tx_ant(mvm->fw), + iwl_fw_valid_rx_ant(mvm->fw)); +} + +#define MAX_NVM_FILE_LEN 16384 + +/* + * HOW TO CREATE THE NVM FILE FORMAT: + * ------------------------------ + * 1. create hex file, format: + * 3800 -> header + * 0000 -> header + * 5a40 -> data + * + * rev - 6 bit (word1) + * len - 10 bit (word1) + * id - 4 bit (word2) + * rsv - 12 bit (word2) + * + * 2. flip 8bits with 8 bits per line to get the right NVM file format + * + * 3. create binary file from the hex file + * + * 4. save as "iNVM_xxx.bin" under /lib/firmware + */ +static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) +{ + int ret, section_id, section_size; + const struct firmware *fw_entry; + const struct { + __le16 word1; + __le16 word2; + u8 data[]; + } *file_sec; + const u8 *eof; + +#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) +#define NVM_WORD2_ID(x) (x >> 12) + + /* + * Obtain NVM image via request_firmware. Since we already used + * request_firmware_nowait() for the firmware binary load and only + * get here after that we assume the NVM request can be satisfied + * synchronously. + */ + ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, + mvm->trans->dev); + if (ret) { + IWL_ERR(mvm, "ERROR: %s isn't available %d\n", + iwlwifi_mod_params.nvm_file, ret); + return ret; + } + + IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", + iwlwifi_mod_params.nvm_file, fw_entry->size); + + if (fw_entry->size < sizeof(*file_sec)) { + IWL_ERR(mvm, "NVM file too small\n"); + ret = -EINVAL; + goto out; + } + + if (fw_entry->size > MAX_NVM_FILE_LEN) { + IWL_ERR(mvm, "NVM file too large\n"); + ret = -EINVAL; + goto out; + } + + eof = fw_entry->data + fw_entry->size; + + file_sec = (void *)fw_entry->data; + + while (true) { + if (file_sec->data > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section header\n"); + ret = -EINVAL; + break; + } + + /* check for EOF marker */ + if (!file_sec->word1 && !file_sec->word2) { + ret = 0; + break; + } + + section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); + section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + + if (section_size > IWL_MAX_NVM_SECTION_SIZE) { + IWL_ERR(mvm, "ERROR - section too large (%d)\n", + section_size); + ret = -EINVAL; + break; + } + + if (!section_size) { + IWL_ERR(mvm, "ERROR - section empty\n"); + ret = -EINVAL; + break; + } + + if (file_sec->data + section_size > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section (%d bytes)\n", + section_size); + ret = -EINVAL; + break; + } + + ret = iwl_nvm_write_section(mvm, section_id, file_sec->data, + section_size); + if (ret < 0) { + IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + break; + } + + /* advance to the next section */ + file_sec = (void *)(file_sec->data + section_size); + } +out: + release_firmware(fw_entry); + return ret; } int iwl_nvm_init(struct iwl_mvm *mvm) @@ -208,6 +382,17 @@ int iwl_nvm_init(struct iwl_mvm *mvm) int ret, i, section; u8 *nvm_buffer, *temp; + /* load external NVM if configured */ + if (iwlwifi_mod_params.nvm_file) { + /* move to External NVM flow */ + ret = iwl_mvm_load_external_nvm(mvm); + if (ret) + return ret; + } + + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + /* TODO: find correct NVM max size for a section */ nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, GFP_KERNEL); @@ -231,8 +416,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm) if (ret < 0) return ret; - ret = 0; mvm->nvm_data = iwl_parse_nvm_sections(mvm); + if (!mvm->nvm_data) + return -ENODATA; - return ret; + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index fe031d304d1e..d7a199b1cdef 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -222,10 +222,14 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), + RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), + RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, + false), + RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), }; #undef RX_HANDLER @@ -288,10 +292,14 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(NET_DETECT_HOTSPOTS_CMD), CMD(NET_DETECT_HOTSPOTS_QUERY_CMD), CMD(CARD_STATE_NOTIFICATION), + CMD(MISSED_BEACONS_NOTIFICATION), CMD(BT_COEX_PRIO_TABLE), CMD(BT_COEX_PROT_ENV), CMD(BT_PROFILE_NOTIFICATION), CMD(BT_CONFIG), + CMD(MCAST_FILTER_CMD), + CMD(REPLY_BEACON_FILTERING_CMD), + CMD(REPLY_THERMAL_MNG_BACKOFF), }; #undef CMD @@ -392,10 +400,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_free; + iwl_mvm_tt_initialize(mvm); + mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); mutex_unlock(&mvm->mutex); - if (err && !iwlmvm_mod_params.init_dbg) { + /* returns 0 if successful, 1 if success but in rfkill */ + if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } @@ -438,10 +449,16 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) iwl_mvm_leds_exit(mvm); + iwl_mvm_tt_exit(mvm); + ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) + kfree(mvm->d3_resume_sram); +#endif + iwl_trans_stop_hw(mvm->trans, true); iwl_phy_db_free(mvm->phy_db); @@ -588,6 +605,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) ieee80211_wake_queue(mvm->hw, mq); } +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) +{ + if (state) + set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + else + clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); +} + static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -597,7 +624,7 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) else clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state); + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); } static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index a28a1d1f23eb..1b4db25d53fb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -195,21 +195,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, return ret; } - -struct phy_ctx_used_data { - unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)]; -}; - -static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx, - void *_data) -{ - struct phy_ctx_used_data *data = _data; - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; - - __set_bit(phy_ctxt->id, data->used); -} - /* * Send a command to add a PHY context based on the current HW configuration. */ @@ -217,34 +202,27 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { - struct phy_ctx_used_data data = { - .used = { }, - }; - - /* - * If this is a regular PHY context (not the ROC one) - * skip the ROC PHY context's ID. - */ - if (ctxt != &mvm->phy_ctxt_roc) - __set_bit(mvm->phy_ctxt_roc.id, data.used); + int ret; + WARN_ON(ctxt->ref); lockdep_assert_held(&mvm->mutex); - ctxt->color++; - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - ieee80211_iter_chan_contexts_atomic( - mvm->hw, iwl_mvm_phy_ctx_used_iter, &data); + ctxt->channel = chandef->chan; + ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD, 0); - ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX); - if (WARN_ONCE(ctxt->id == NUM_PHY_CTX, - "Failed to init PHY context - no free ID!\n")) - return -EIO; - } + return ret; +} - ctxt->channel = chandef->chan; - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); +/* + * Update the number of references to the given PHY context. This is valid only + * in case the PHY context was already created, i.e., its reference count > 0. + */ +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +{ + lockdep_assert_held(&mvm->mutex); + ctxt->ref++; } /* @@ -264,23 +242,12 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, FW_CTXT_ACTION_MODIFY, 0); } -/* - * Send a command to the FW to remove the given phy context. - * Once the command is sent, regardless of success or failure, the context is - * marked as invalid - */ -void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { - struct iwl_phy_context_cmd cmd; - int ret; - lockdep_assert_held(&mvm->mutex); - iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0); - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC, - sizeof(struct iwl_phy_context_cmd), - &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n", - ctxt->id); + if (WARN_ON_ONCE(!ctxt)) + return; + + ctxt->ref--; } diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index ed77e437aac4..67cf24aa72f9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -75,6 +75,54 @@ #define POWER_KEEP_ALIVE_PERIOD_SEC 25 +static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd) +{ + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC, + sizeof(struct iwl_beacon_filter_cmd), cmd); + + if (!ret) { + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", + cmd->ba_enable_beacon_abort); + IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", + cmd->ba_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", + cmd->bf_debug_flag); + IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", + cmd->bf_enable_beacon_filter); + IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", + cmd->bf_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", + cmd->bf_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", + cmd->bf_roaming_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", + cmd->bf_roaming_state); + IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n", + cmd->bf_temperature_delta); + } + return ret; +} + +static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = 1, + .ba_enable_beacon_abort = enable, + }; + + if (!mvmvif->bf_enabled) + return 0; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); +} + static void iwl_mvm_power_log(struct iwl_mvm *mvm, struct iwl_powertable_cmd *cmd) { @@ -91,6 +139,9 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, le32_to_cpu(cmd->tx_data_timeout)); IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", cmd->lprx_rssi_threshold); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) + IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", + le32_to_cpu(cmd->skip_dtim_periods)); } } @@ -103,6 +154,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int dtimper, dtimper_msec; int keep_alive; bool radar_detect = false; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); /* * Regardless of power management state the driver must set @@ -116,6 +169,11 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif if (!vif->bss_conf.ps) return; @@ -135,8 +193,11 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* Check skip over DTIM conditions */ if (!radar_detect && (dtimper <= 10) && - (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) + (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || + mvm->cur_ucode == IWL_UCODE_WOWLAN)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + cmd->skip_dtim_periods = cpu_to_le32(3); + } /* Check that keep alive period is at least 3 * DTIM */ dtimper_msec = dtimper * vif->bss_conf.beacon_int; @@ -145,12 +206,41 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); cmd->keep_alive_seconds = keep_alive; - cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { + cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + } else { + cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + } + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) + cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { + if (mvmvif->dbgfs_pm.skip_over_dtim) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) + cmd->rx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) + cmd->tx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) + cmd->skip_dtim_periods = + cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods); +#endif /* CONFIG_IWLWIFI_DEBUGFS */ } int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + int ret; + bool ba_enable; struct iwl_powertable_cmd cmd = {}; if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) @@ -159,13 +249,22 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_power_build_cmd(mvm, vif, &cmd); iwl_mvm_power_log(mvm, &cmd); - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, + sizeof(cmd), &cmd); + if (ret) + return ret; + + ba_enable = !!(cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); + + return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); } int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_powertable_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; @@ -173,8 +272,82 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif iwl_mvm_power_log(mvm, &cmd); return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, sizeof(cmd), &cmd); } + +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) + cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) + cmd->bf_roaming_energy_delta = + dbgfs_bf->bf_roaming_energy_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) + cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA) + cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) + cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) + cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) + cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) + cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort; +} +#endif + +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = 1, + }; + int ret; + + if (mvmvif != mvm->bf_allowed_vif || + vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + + if (!ret) + mvmvif->bf_enabled = true; + + return ret; +} + +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_beacon_filter_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + + if (!ret) + mvmvif->bf_enabled = false; + + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 55334d542e26..6a050c69e7d0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -3080,3 +3080,29 @@ void iwl_mvm_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_mvm_ops); } + +/** + * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable + * Tx protection, according to this rquest and previous requests, + * and send the LQ command. + * @lq: The LQ command + * @mvmsta: The station + * @enable: Enable Tx protection? + */ +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, + struct iwl_mvm_sta *mvmsta, bool enable) +{ + lockdep_assert_held(&mvm->mutex); + + if (enable) { + if (mvmsta->tx_protection == 0) + lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + mvmsta->tx_protection++; + } else { + mvmsta->tx_protection--; + if (mvmsta->tx_protection == 0) + lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK; + } + + return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 219c6857cc0f..f66155a57238 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -390,4 +390,9 @@ extern int iwl_mvm_rate_control_register(void); */ extern void iwl_mvm_rate_control_unregister(void); +struct iwl_mvm_sta; + +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, + struct iwl_mvm_sta *mvmsta, bool enable); + #endif /* __rs__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 4dfc21a3e83e..e4930d5027d2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -363,3 +363,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, rxb, &rx_status); return 0; } + +/* + * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler + * + * TODO: This handler is implemented partially. + * It only gets the NIC's temperature. + */ +int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_notif_statistics *stats = (void *)&pkt->data; + struct mvm_statistics_general_common *common = &stats->general.common; + + if (mvm->temperature != le32_to_cpu(common->temperature)) { + mvm->temperature = le32_to_cpu(common->temperature); + iwl_mvm_tt_handler(mvm); + } + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 0fd96e4da461..2278858d5658 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -64,6 +64,7 @@ #include "mvm.h" #include "sta.h" +#include "rs.h" static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) { @@ -217,9 +218,11 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvmvif->color); mvm_sta->vif = vif; mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + mvm_sta->tx_protection = 0; + mvm_sta->tt_tx_protection = false; /* HW restart, don't assume the memory has been zeroed */ - atomic_set(&mvm_sta->pending_frames, 0); + atomic_set(&mvm->pending_frames[sta_id], 0); mvm_sta->tid_disable_agg = 0; mvm_sta->tfd_queue_msk = 0; for (i = 0; i < IEEE80211_NUM_ACS; i++) @@ -407,14 +410,21 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, } /* + * Make sure that the tx response code sees the station as -EBUSY and + * calls the drain worker. + */ + spin_lock_bh(&mvm_sta->lock); + /* * There are frames pending on the AC queues for this station. * We need to wait until all the frames are drained... */ - if (atomic_read(&mvm_sta->pending_frames)) { - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); + if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) { rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], ERR_PTR(-EBUSY)); + spin_unlock_bh(&mvm_sta->lock); + ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); } else { + spin_unlock_bh(&mvm_sta->lock); ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); } @@ -791,21 +801,23 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, min(mvmsta->max_agg_bufsize, buf_size); mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; + IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); + if (mvm->cfg->ht_params->use_rts_for_aggregation) { /* * switch to RTS/CTS if it is the prefer protection * method for HT traffic + * this function also sends the LQ command */ - mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, + mvmsta, true); /* * TODO: remove the TLC_RTS flag when we tear down the last * AGG session (agg_tids_count in DVM) */ } - IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 12abd2d71835..3efa0a0cc987 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -274,8 +274,9 @@ struct iwl_mvm_tid_data { * @bt_reduced_txpower: is reduced tx power enabled for this station * @lock: lock to protect the whole struct. Since %tid_data is access from Tx * and from Tx response flow, it needs a spinlock. - * @pending_frames: number of frames for this STA on the shared Tx queues. * @tid_data: per tid data. Look at %iwl_mvm_tid_data. + * @tx_protection: reference counter for controlling the Tx protection. + * @tt_tx_protection: is thermal throttling enable Tx protection? * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -290,7 +291,6 @@ struct iwl_mvm_sta { u8 max_agg_bufsize; bool bt_reduced_txpower; spinlock_t lock; - atomic_t pending_frames; struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; struct iwl_lq_sta lq_sta; struct ieee80211_vif *vif; @@ -298,6 +298,10 @@ struct iwl_mvm_sta { #ifdef CONFIG_PM_SLEEP u16 last_seq_ctl; #endif + + /* Temporary, until the new TLC will control the Tx protection */ + s8 tx_protection; + bool tt_tx_protection; }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c new file mode 100644 index 000000000000..4665fc033c17 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -0,0 +1,509 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include "mvm.h" +#include "iwl-config.h" +#include "iwl-io.h" +#include "iwl-csr.h" +#include "iwl-prph.h" + +#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/ +/* VBG - Voltage Band Gap error data (temperature offset) */ +#define OTP_WP_DTS_VBG (OTP_DTS_DIODE_DEVIATION + 2) +#define MEAS_VBG_MIN_VAL 2300 +#define MEAS_VBG_MAX_VAL 3000 +#define MEAS_VBG_DEFAULT_VAL 2700 +#define DTS_DIODE_VALID(flags) (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE) +#define MIN_TEMPERATURE 0 +#define MAX_TEMPERATURE 125 +#define TEMPERATURE_ERROR (MAX_TEMPERATURE + 1) +#define PTAT_DIGITAL_VALUE_MIN_VALUE 0 +#define PTAT_DIGITAL_VALUE_MAX_VALUE 0xFF +#define DTS_VREFS_NUM 5 +static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags) +{ + return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >> + DTS_DIODE_REG_FLAGS_VREFS_ID_POS; +} + +#define CALC_VREFS_MIN_DIFF 43 +#define CALC_VREFS_MAX_DIFF 51 +#define CALC_LUT_SIZE (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF) +#define CALC_LUT_INDEX_OFFSET CALC_VREFS_MIN_DIFF +#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET 23 + +/* + * @digital_value: The diode's digital-value sampled (temperature/voltage) + * @vref_low: The lower voltage-reference (the vref just below the diode's + * sampled digital-value) + * @vref_high: The higher voltage-reference (the vref just above the diode's + * sampled digital-value) + * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref) + * bits[6:2]: Reserved. + * bits[7:7]: Indicates completion of at least 1 successful sample + * since last DTS reset. + */ +struct iwl_mvm_dts_diode_bits { + u8 digital_value; + u8 vref_low; + u8 vref_high; + u8 flags; +} __packed; + +union dts_diode_results { + u32 reg_value; + struct iwl_mvm_dts_diode_bits bits; +} __packed; + +static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm) +{ + struct iwl_nvm_section calib_sec; + const __le16 *calib; + u16 vbg; + + /* TODO: move parsing to NVM code */ + calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION]; + calib = (__le16 *)calib_sec.data; + + vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]); + + if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL) + vbg = MEAS_VBG_DEFAULT_VAL; + + return vbg; +} + +static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm) +{ + const u8 *calib; + u8 ptat, pa1, pa2, median; + + /* TODO: move parsing to NVM code */ + calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data; + ptat = calib[OTP_DTS_DIODE_DEVIATION]; + pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1]; + pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2]; + + /* get the median: */ + if (ptat > pa1) { + if (ptat > pa2) + median = (pa1 > pa2) ? pa1 : pa2; + else + median = ptat; + } else { + if (pa1 > pa2) + median = (ptat > pa2) ? ptat : pa2; + else + median = pa1; + } + + return ptat - median; +} + +static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value) +{ + /* Calibrate the PTAT digital value, based on PTAT deviation data: */ + s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm); + + if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE) + new_val = PTAT_DIGITAL_VALUE_MAX_VALUE; + else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE) + new_val = PTAT_DIGITAL_VALUE_MIN_VALUE; + + return new_val; +} + +static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm, + union dts_diode_results *avg_ptat) +{ + u8 vrefs_results[DTS_VREFS_NUM]; + u8 low_vref_index = 0, flags; + u32 reg; + + reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG); + memcpy(vrefs_results, ®, sizeof(reg)); + reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG); + vrefs_results[4] = reg & 0xff; + + if (avg_ptat->bits.digital_value < vrefs_results[0] || + avg_ptat->bits.digital_value > vrefs_results[4]) + return false; + + if (avg_ptat->bits.digital_value > vrefs_results[3]) + low_vref_index = 3; + else if (avg_ptat->bits.digital_value > vrefs_results[2]) + low_vref_index = 2; + else if (avg_ptat->bits.digital_value > vrefs_results[1]) + low_vref_index = 1; + + avg_ptat->bits.vref_low = vrefs_results[low_vref_index]; + avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1]; + flags = avg_ptat->bits.flags; + avg_ptat->bits.flags = + (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) | + (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID); + return true; +} + +/* + * return true it the results are valid, and false otherwise. + */ +static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm, + union dts_diode_results *avg_ptat) +{ + u32 reg; + u8 tmp; + + /* fill the diode value and pass_once with avg-reg results */ + reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG); + reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE; + avg_ptat->reg_value = reg; + + /* calibrate the PTAT digital value */ + tmp = avg_ptat->bits.digital_value; + tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp); + avg_ptat->bits.digital_value = tmp; + + /* + * fill vrefs fields, based on the avgVrefs results + * and the diode value + */ + return dts_get_adjacent_vrefs(mvm, avg_ptat) && + DTS_DIODE_VALID(avg_ptat->bits.flags); +} + +static s32 calculate_nic_temperature(union dts_diode_results avg_ptat, + u16 volt_band_gap) +{ + u32 tmp_result; + u8 vrefs_diff; + /* + * For temperature calculation (at the end, shift right by 23) + * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) } + * (D2-D1) == 43 44 45 46 47 48 49 50 51 + */ + static const u16 calc_lut[CALC_LUT_SIZE] = { + 2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828, + }; + + /* + * The diff between the high and low voltage-references is assumed + * to be strictly be in range of [60,68] + */ + vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low; + + if (vrefs_diff < CALC_VREFS_MIN_DIFF || + vrefs_diff > CALC_VREFS_MAX_DIFF) + return TEMPERATURE_ERROR; + + /* calculate the result: */ + tmp_result = + vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9); + tmp_result += avg_ptat.bits.digital_value; + tmp_result -= avg_ptat.bits.vref_high; + + /* multiply by the LUT value (based on the diff) */ + tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET]; + + /* + * Get the BandGap (the voltage refereces source) error data + * (temperature offset) + */ + tmp_result *= volt_band_gap; + + /* + * here, tmp_result value can be up to 32-bits. We want to right-shift + * it *without* sign-extend. + */ + tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET; + + /* + * at this point, tmp_result should be in the range: + * 200 <= tmp_result <= 365 + */ + return (s16)tmp_result - 240; +} + +static s32 check_nic_temperature(struct iwl_mvm *mvm) +{ + u16 volt_band_gap; + union dts_diode_results avg_ptat; + + volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm); + + /* disable DTS */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0); + + /* SV initialization */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1); + iwl_write_prph(mvm->trans, DTSC_CFG_MODE, + DTSC_CFG_MODE_PERIODIC); + + /* wait for results */ + msleep(100); + if (!dts_read_ptat_avg_results(mvm, &avg_ptat)) + return TEMPERATURE_ERROR; + + /* disable DTS */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0); + + return calculate_nic_temperature(avg_ptat, volt_band_gap); +} + +static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) +{ + u32 duration = mvm->thermal_throttle.params->ct_kill_duration; + + IWL_ERR(mvm, "Enter CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, true); + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies_relative(duration * HZ)); +} + +static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) +{ + IWL_ERR(mvm, "Exit CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, false); +} + +static void check_exit_ctkill(struct work_struct *work) +{ + struct iwl_mvm_tt_mgmt *tt; + struct iwl_mvm *mvm; + u32 duration; + s32 temp; + + tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); + mvm = container_of(tt, struct iwl_mvm, thermal_throttle); + + duration = tt->params->ct_kill_duration; + + iwl_trans_start_hw(mvm->trans); + temp = check_nic_temperature(mvm); + iwl_trans_stop_hw(mvm->trans, false); + + if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) { + IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n"); + goto reschedule; + } + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); + + if (temp <= tt->params->ct_kill_exit) { + iwl_mvm_exit_ctkill(mvm); + return; + } + +reschedule: + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies(duration * HZ)); +} + +static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + enum ieee80211_smps_mode smps_mode; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->thermal_throttle.dynamic_smps) + smps_mode = IEEE80211_SMPS_DYNAMIC; + else + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); +} + +static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int i, err; + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + mvmsta = (void *)sta->drv_priv; + if (enable == mvmsta->tt_tx_protection) + continue; + err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, + mvmsta, enable); + if (err) { + IWL_ERR(mvm, "Failed to %s Tx protection\n", + enable ? "enable" : "disable"); + } else { + IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", + enable ? "Enable" : "Disable"); + mvmsta->tt_tx_protection = enable; + } + } +} + +static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_THERMAL_MNG_BACKOFF, + .len = { sizeof(u32), }, + .data = { &backoff, }, + .flags = CMD_SYNC, + }; + + if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { + IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", + backoff); + mvm->thermal_throttle.tx_backoff = backoff; + } else { + IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); + } +} + +void iwl_mvm_tt_handler(struct iwl_mvm *mvm) +{ + const struct iwl_tt_params *params = mvm->thermal_throttle.params; + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + s32 temperature = mvm->temperature; + int i; + u32 tx_backoff; + + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); + + if (params->support_ct_kill && temperature >= params->ct_kill_entry) { + iwl_mvm_enter_ctkill(mvm); + return; + } + + if (params->support_dynamic_smps) { + if (!tt->dynamic_smps && + temperature >= params->dynamic_smps_entry) { + IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); + tt->dynamic_smps = true; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + } else if (tt->dynamic_smps && + temperature <= params->dynamic_smps_exit) { + IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); + tt->dynamic_smps = false; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + } + } + + if (params->support_tx_protection) { + if (temperature >= params->tx_protection_entry) + iwl_mvm_tt_tx_protection(mvm, true); + else if (temperature <= params->tx_protection_exit) + iwl_mvm_tt_tx_protection(mvm, false); + } + + if (params->support_tx_backoff) { + tx_backoff = 0; + for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { + if (temperature < params->tx_backoff[i].temperature) + break; + tx_backoff = params->tx_backoff[i].backoff; + } + if (tt->tx_backoff != tx_backoff) + iwl_mvm_tt_tx_backoff(mvm, tx_backoff); + } +} + +static const struct iwl_tt_params iwl7000_tt_params = { + .ct_kill_entry = 118, + .ct_kill_exit = 96, + .ct_kill_duration = 5, + .dynamic_smps_entry = 114, + .dynamic_smps_exit = 110, + .tx_protection_entry = 114, + .tx_protection_exit = 108, + .tx_backoff = { + {.temperature = 112, .backoff = 200}, + {.temperature = 113, .backoff = 600}, + {.temperature = 114, .backoff = 1200}, + {.temperature = 115, .backoff = 2000}, + {.temperature = 116, .backoff = 4000}, + {.temperature = 117, .backoff = 10000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + + IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); + tt->params = &iwl7000_tt_params; + INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); +} + +void iwl_mvm_tt_exit(struct iwl_mvm *mvm) +{ + cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); + IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 479074303bd7..f212f16502ff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -416,9 +416,8 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, spin_unlock(&mvmsta->lock); - if (mvmsta->vif->type == NL80211_IFTYPE_AP && - txq_id < IWL_MVM_FIRST_AGG_QUEUE) - atomic_inc(&mvmsta->pending_frames); + if (txq_id < IWL_MVM_FIRST_AGG_QUEUE) + atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); return 0; @@ -680,16 +679,41 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, /* * If the txq is not an AMPDU queue, there is no chance we freed * several skbs. Check that out... - * If there are no pending frames for this STA, notify mac80211 that - * this station can go to sleep in its STA table. */ - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && mvmsta && - !WARN_ON(skb_freed > 1) && - mvmsta->vif->type == NL80211_IFTYPE_AP && - atomic_sub_and_test(skb_freed, &mvmsta->pending_frames)) { - ieee80211_sta_block_awake(mvm->hw, sta, false); - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); + if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) && + atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) { + if (mvmsta) { + /* + * If there are no pending frames for this STA, notify + * mac80211 that this station can go to sleep in its + * STA table. + */ + if (mvmsta->vif->type == NL80211_IFTYPE_AP) + ieee80211_sta_block_awake(mvm->hw, sta, false); + /* + * We might very well have taken mvmsta pointer while + * the station was being removed. The remove flow might + * have seen a pending_frame (because we didn't take + * the lock) even if now the queues are drained. So make + * really sure now that this the station is not being + * removed. If it is, run the drain worker to remove it. + */ + spin_lock_bh(&mvmsta->lock); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (IS_ERR_OR_NULL(sta)) { + /* + * Station disappeared in the meantime: + * so we are draining. + */ + set_bit(sta_id, mvm->sta_drained); + schedule_work(&mvm->sta_drained_wk); + } + spin_unlock_bh(&mvmsta->lock); + } else if (!mvmsta) { + /* Tx response without STA, so we are draining */ + set_bit(sta_id, mvm->sta_drained); + schedule_work(&mvm->sta_drained_wk); + } } rcu_read_unlock(); diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 687b34e387ac..c9b44ab4af07 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -471,3 +471,34 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, return iwl_mvm_send_cmd(mvm, &cmd); } + +/** + * iwl_mvm_update_smps - Get a requst to change the SMPS mode + * @req_type: The part of the driver who call for a change. + * @smps_requests: The request to change the SMPS mode. + * + * Get a requst to change the SMPS mode, + * and change it according to all other requests in the driver. + */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request) +{ + struct iwl_mvm_vif *mvmvif; + enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; + int i; + + lockdep_assert_held(&mvm->mutex); + mvmvif = iwl_mvm_vif_from_mac80211(vif); + mvmvif->smps_requests[req_type] = smps_request; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { + smps_mode = IEEE80211_SMPS_STATIC; + break; + } + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + smps_mode = IEEE80211_SMPS_DYNAMIC; + } + + ieee80211_request_smps(vif, smps_mode); +} diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 8cb53ec2b77b..db7bdd35a9c5 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -256,10 +256,54 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { /* 7000 Series */ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)}, + +/* 3160 Series */ + {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, {0} }; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 50ba0a468f94..0b021305eedf 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -405,20 +405,27 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, { u8 *v_addr; dma_addr_t p_addr; - u32 offset; + u32 offset, chunk_sz = section->len; int ret = 0; IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", section_num); - v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL); - if (!v_addr) - return -ENOMEM; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, + GFP_KERNEL | __GFP_NOWARN); + if (!v_addr) { + IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); + chunk_sz = PAGE_SIZE; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, + &p_addr, GFP_KERNEL); + if (!v_addr) + return -ENOMEM; + } - for (offset = 0; offset < section->len; offset += PAGE_SIZE) { + for (offset = 0; offset < section->len; offset += chunk_sz) { u32 copy_size; - copy_size = min_t(u32, PAGE_SIZE, section->len - offset); + copy_size = min_t(u32, chunk_sz, section->len - offset); memcpy(v_addr, (u8 *)section->data + offset, copy_size); ret = iwl_pcie_load_firmware_chunk(trans, @@ -432,7 +439,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, } } - dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr); + dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); return ret; } @@ -573,10 +580,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) { - /* let the ucode operate on its own */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - iwl_disable_interrupts(trans); iwl_pcie_disable_ict(trans); @@ -636,9 +639,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, return ret; } - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - *status = IWL_D3_STATUS_ALIVE; return 0; } @@ -917,11 +917,11 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, } static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) + const void *buf, int dwords) { unsigned long flags; int offs, ret = 0; - u32 *vals = buf; + const u32 *vals = buf; if (iwl_trans_grab_nic_access(trans, false, &flags)) { iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c5e30294c5ac..bf5f8246385e 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1045,6 +1045,10 @@ static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans, (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn) { @@ -1518,6 +1522,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { IWL_ERR(trans, "FW error in SYNC CMD %s\n", get_cmd_string(trans_pcie, cmd->id)); + dump_stack(); ret = -EIO; goto cancel; } @@ -1564,7 +1569,8 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) return -EIO; - if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans_pcie->status)) { IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", cmd->id); return -ERFKILL; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index af79daa331d0..00a82817eb6b 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2282,9 +2282,6 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) if (wdev->netdev->reg_state == NETREG_REGISTERED) unregister_netdevice(wdev->netdev); - if (wdev->netdev->reg_state == NETREG_UNREGISTERED) - free_netdev(wdev->netdev); - /* Clear the priv in adapter */ priv->netdev = NULL; diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 4a32f2795c64..2d761477d15e 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1192,6 +1192,7 @@ mwifiex_process_hs_config(struct mwifiex_adapter *adapter) adapter->if_ops.wakeup(adapter); adapter->hs_activated = false; adapter->is_hs_configured = false; + adapter->is_suspended = false; mwifiex_hs_activated_event(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), false); diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 29d83f0c1fb8..5bc7ef8d04d6 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -669,6 +669,7 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) { dev->netdev_ops = &mwifiex_netdev_ops; + dev->destructor = free_netdev; /* Initialize private structure */ priv->current_key_index = 0; priv->media_connected = false; diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 311d0b26b81c..1a8a19dbd635 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -96,7 +96,7 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, } else { /* Multicast */ priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; - if (mcast_list->mode == MWIFIEX_MULTICAST_MODE) { + if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { dev_dbg(priv->adapter->dev, "info: Enabling All Multicast!\n"); priv->curr_pkt_filter |= @@ -108,20 +108,11 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, dev_dbg(priv->adapter->dev, "info: Set multicast list=%d\n", mcast_list->num_multicast_addr); - /* Set multicast addresses to firmware */ - if (old_pkt_filter == priv->curr_pkt_filter) { - /* Send request to firmware */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list); - } else { - /* Send request to firmware */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list); - } + /* Send multicast addresses to firmware */ + ret = mwifiex_send_cmd_async(priv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + mcast_list); } } } |