aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorLilach Edelstein <lilach.edelstein@intel.com>2013-10-06 07:03:32 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2013-12-17 12:39:37 -0500
commit1f3b0ff8eccefb0a22398bb044fa12f7e3f6f058 (patch)
tree2b2515e28295c3f5d61b5b74236505b440b2a402 /drivers/net/wireless
parent710e4d08f2ab6eed7950884ab30e684704e325ca (diff)
iwlwifi: mvm: Add Smart FIFO support
Send firmware a Smart FIFO Configuration host command to allow interrupt coalescing. The smart FIFO is enabled when there is only one bound interface (other than p2p devices which are ignored) and it is of type station, and activated while the station is associated. Smart Fifo allows aggragations of DMA transactions and by that causes processor and memory controller to stay for a longer time on lower c-states, thus saving platform power. Firmware relies on driver to activate and disable it. Signed-off-by: Lilach Edelstein <lilach.edelstein@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/binding.c16
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h62
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c21
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h7
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sf.c291
8 files changed, 402 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index 285d8c7486f5..3ea47ab27337 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -1,6 +1,6 @@
1obj-$(CONFIG_IWLMVM) += iwlmvm.o 1obj-$(CONFIG_IWLMVM) += iwlmvm.o
2iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o 2iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o 3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
4iwlmvm-y += scan.o time-event.o rs.o 4iwlmvm-y += scan.o time-event.o rs.o
5iwlmvm-y += power.o power_legacy.o bt-coex.o 5iwlmvm-y += power.o power_legacy.o bt-coex.o
6iwlmvm-y += led.o tt.o 6iwlmvm-y += led.o tt.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c
index 93fd1457954b..57d3eed86efa 100644
--- a/drivers/net/wireless/iwlwifi/mvm/binding.c
+++ b/drivers/net/wireless/iwlwifi/mvm/binding.c
@@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
183 if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) 183 if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
184 return -EINVAL; 184 return -EINVAL;
185 185
186 /*
187 * Update SF - Disable if needed. if this fails, SF might still be on
188 * while many macs are bound, which is forbidden - so fail the binding.
189 */
190 if (iwl_mvm_sf_update(mvm, vif, false))
191 return -EINVAL;
192
186 return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); 193 return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
187} 194}
188 195
189int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 196int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
190{ 197{
191 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 198 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
199 int ret;
192 200
193 if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) 201 if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
194 return -EINVAL; 202 return -EINVAL;
195 203
196 return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); 204 ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
205
206 if (!ret)
207 if (iwl_mvm_sf_update(mvm, vif, true))
208 IWL_ERR(mvm, "Failed to update SF state\n");
209
210 return ret;
197} 211}
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index bad5a552dd8d..3af413386ad9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -183,6 +183,7 @@ enum {
183 BT_PROFILE_NOTIFICATION = 0xce, 183 BT_PROFILE_NOTIFICATION = 0xce,
184 BT_COEX_CI = 0x5d, 184 BT_COEX_CI = 0x5d,
185 185
186 REPLY_SF_CFG_CMD = 0xd1,
186 REPLY_BEACON_FILTERING_CMD = 0xd2, 187 REPLY_BEACON_FILTERING_CMD = 0xd2,
187 188
188 REPLY_DEBUG_CMD = 0xf0, 189 REPLY_DEBUG_CMD = 0xf0,
@@ -1363,4 +1364,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
1363 struct mvm_statistics_general general; 1364 struct mvm_statistics_general general;
1364} __packed; 1365} __packed;
1365 1366
1367/***********************************
1368 * Smart Fifo API
1369 ***********************************/
1370/* Smart Fifo state */
1371enum iwl_sf_state {
1372 SF_LONG_DELAY_ON = 0, /* should never be called by driver */
1373 SF_FULL_ON,
1374 SF_UNINIT,
1375 SF_INIT_OFF,
1376 SF_HW_NUM_STATES
1377};
1378
1379/* Smart Fifo possible scenario */
1380enum iwl_sf_scenario {
1381 SF_SCENARIO_SINGLE_UNICAST,
1382 SF_SCENARIO_AGG_UNICAST,
1383 SF_SCENARIO_MULTICAST,
1384 SF_SCENARIO_BA_RESP,
1385 SF_SCENARIO_TX_RESP,
1386 SF_NUM_SCENARIO
1387};
1388
1389#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */
1390#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */
1391
1392/* smart FIFO default values */
1393#define SF_W_MARK_SISO 4096
1394#define SF_W_MARK_MIMO2 8192
1395#define SF_W_MARK_MIMO3 6144
1396#define SF_W_MARK_LEGACY 4096
1397#define SF_W_MARK_SCAN 4096
1398
1399/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
1400#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */
1401#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */
1402#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */
1403#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */
1404#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */
1405#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */
1406#define SF_BA_IDLE_TIMER 320 /* 300 uSec */
1407#define SF_BA_AGING_TIMER 2016 /* 2 mSec */
1408#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */
1409#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */
1410
1411#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */
1412
1413/**
1414 * Smart Fifo configuration command.
1415 * @state: smart fifo state, types listed in iwl_sf_sate.
1416 * @watermark: Minimum allowed availabe free space in RXF for transient state.
1417 * @long_delay_timeouts: aging and idle timer values for each scenario
1418 * in long delay state.
1419 * @full_on_timeouts: timer values for each scenario in full on state.
1420 */
1421struct iwl_sf_cfg_cmd {
1422 enum iwl_sf_state state;
1423 __le32 watermark[SF_TRANSIENT_STATES_NUMBER];
1424 __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
1425 __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
1426} __packed; /* SF_CFG_API_S_VER_2 */
1427
1366#endif /* __fw_api_h__ */ 1428#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 70e5297646b2..b2bfa732a594 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -386,6 +386,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
386 goto error; 386 goto error;
387 } 387 }
388 388
389 ret = iwl_mvm_sf_update(mvm, NULL, false);
390 if (ret)
391 IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
392
389 ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); 393 ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
390 if (ret) 394 if (ret)
391 goto error; 395 goto error;
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index afc4419be46d..f0f8b2c7ebef 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -850,7 +850,16 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
850 iwl_mvm_protect_session(mvm, vif, dur, dur, 850 iwl_mvm_protect_session(mvm, vif, dur, dur,
851 5 * dur); 851 5 * dur);
852 } 852 }
853
854 iwl_mvm_sf_update(mvm, vif, false);
853 } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { 855 } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
856 /*
857 * If update fails - SF might be running in associated
858 * mode while disassociated - which is forbidden.
859 */
860 WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
861 "Failed to update SF upon disassociation\n");
862
854 /* remove AP station now that the MAC is unassoc */ 863 /* remove AP station now that the MAC is unassoc */
855 ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); 864 ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
856 if (ret) 865 if (ret)
@@ -1200,6 +1209,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1200 return 0; 1209 return 0;
1201} 1210}
1202 1211
1212static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
1213 struct ieee80211_vif *vif,
1214 struct ieee80211_sta *sta, u32 changed)
1215{
1216 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1217
1218 if (vif->type == NL80211_IFTYPE_STATION &&
1219 changed & IEEE80211_RC_NSS_CHANGED)
1220 iwl_mvm_sf_update(mvm, vif, false);
1221}
1222
1203static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, 1223static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
1204 struct ieee80211_vif *vif, u16 ac, 1224 struct ieee80211_vif *vif, u16 ac,
1205 const struct ieee80211_tx_queue_params *params) 1225 const struct ieee80211_tx_queue_params *params)
@@ -1765,6 +1785,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
1765 .sta_notify = iwl_mvm_mac_sta_notify, 1785 .sta_notify = iwl_mvm_mac_sta_notify,
1766 .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, 1786 .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
1767 .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, 1787 .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
1788 .sta_rc_update = iwl_mvm_sta_rc_update,
1768 .conf_tx = iwl_mvm_mac_conf_tx, 1789 .conf_tx = iwl_mvm_mac_conf_tx,
1769 .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, 1790 .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
1770 .sched_scan_start = iwl_mvm_mac_sched_scan_start, 1791 .sched_scan_start = iwl_mvm_mac_sched_scan_start,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 7dc57cfe5803..bd072c7955be 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -489,6 +489,9 @@ struct iwl_mvm {
489 u8 scan_last_antenna_idx; /* to toggle TX between antennas */ 489 u8 scan_last_antenna_idx; /* to toggle TX between antennas */
490 u8 mgmt_last_antenna_idx; 490 u8 mgmt_last_antenna_idx;
491 491
492 /* last smart fifo state that was successfully sent to firmware */
493 enum iwl_sf_state sf_state;
494
492#ifdef CONFIG_IWLWIFI_DEBUGFS 495#ifdef CONFIG_IWLWIFI_DEBUGFS
493 struct dentry *debugfs_dir; 496 struct dentry *debugfs_dir;
494 u32 dbgfs_sram_offset, dbgfs_sram_len; 497 u32 dbgfs_sram_offset, dbgfs_sram_len;
@@ -869,4 +872,8 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
869void iwl_mvm_tt_exit(struct iwl_mvm *mvm); 872void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
870void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); 873void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
871 874
875/* smart fifo */
876int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
877 bool added_vif);
878
872#endif /* __IWL_MVM_H__ */ 879#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index d86083c6f445..cae6123d25c3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -359,6 +359,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
359 mvm->aux_queue = 11; 359 mvm->aux_queue = 11;
360 mvm->first_agg_queue = 12; 360 mvm->first_agg_queue = 12;
361 } 361 }
362 mvm->sf_state = SF_UNINIT;
362 363
363 mutex_init(&mvm->mutex); 364 mutex_init(&mvm->mutex);
364 spin_lock_init(&mvm->async_handlers_lock); 365 spin_lock_init(&mvm->async_handlers_lock);
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
new file mode 100644
index 000000000000..97bb3c3e75ce
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/sf.c
@@ -0,0 +1,291 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2013 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63#include "mvm.h"
64
65/* For counting bound interfaces */
66struct iwl_mvm_active_iface_iterator_data {
67 struct ieee80211_vif *ignore_vif;
68 u8 sta_vif_ap_sta_id;
69 enum iwl_sf_state sta_vif_state;
70 int num_active_macs;
71};
72
73/*
74 * Count bound interfaces which are not p2p, besides data->ignore_vif.
75 * data->station_vif will point to one bound vif of type station, if exists.
76 */
77static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
78 struct ieee80211_vif *vif)
79{
80 struct iwl_mvm_active_iface_iterator_data *data = _data;
81 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
82
83 if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
84 vif->type == NL80211_IFTYPE_P2P_DEVICE)
85 return;
86
87 data->num_active_macs++;
88
89 if (vif->type == NL80211_IFTYPE_STATION) {
90 data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
91 if (vif->bss_conf.assoc)
92 data->sta_vif_state = SF_FULL_ON;
93 else
94 data->sta_vif_state = SF_INIT_OFF;
95 }
96}
97
98/*
99 * Aging and idle timeouts for the different possible scenarios
100 * in SF_FULL_ON state.
101 */
102static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
103 {
104 cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
105 cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
106 },
107 {
108 cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
109 cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
110 },
111 {
112 cpu_to_le32(SF_MCAST_AGING_TIMER),
113 cpu_to_le32(SF_MCAST_IDLE_TIMER)
114 },
115 {
116 cpu_to_le32(SF_BA_AGING_TIMER),
117 cpu_to_le32(SF_BA_IDLE_TIMER)
118 },
119 {
120 cpu_to_le32(SF_TX_RE_AGING_TIMER),
121 cpu_to_le32(SF_TX_RE_IDLE_TIMER)
122 },
123};
124
125static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
126 struct ieee80211_sta *sta)
127{
128 int i, j, watermark;
129
130 sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
131
132 /*
133 * If we are in association flow - check antenna configuration
134 * capabilities of the AP station, and choose the watermark accordingly.
135 */
136 if (sta) {
137 if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
138 switch (sta->rx_nss) {
139 case 1:
140 watermark = SF_W_MARK_SISO;
141 break;
142 case 2:
143 watermark = SF_W_MARK_MIMO2;
144 break;
145 default:
146 watermark = SF_W_MARK_MIMO3;
147 break;
148 }
149 } else {
150 watermark = SF_W_MARK_LEGACY;
151 }
152 /* default watermark value for unassociated mode. */
153 } else {
154 watermark = SF_W_MARK_MIMO2;
155 }
156 sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
157
158 for (i = 0; i < SF_NUM_SCENARIO; i++) {
159 for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
160 sf_cmd->long_delay_timeouts[i][j] =
161 cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
162 }
163 }
164 BUILD_BUG_ON(sizeof(sf_full_timeout) !=
165 sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
166
167 memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
168 sizeof(sf_full_timeout));
169}
170
171static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
172 enum iwl_sf_state new_state)
173{
174 struct iwl_sf_cfg_cmd sf_cmd = {
175 .state = new_state,
176 };
177 struct ieee80211_sta *sta;
178 int ret = 0;
179
180 /*
181 * If an associated AP sta changed its antenna configuration, the state
182 * will remain FULL_ON but SF parameters need to be reconsidered.
183 */
184 if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
185 return 0;
186
187 switch (new_state) {
188 case SF_UNINIT:
189 break;
190 case SF_FULL_ON:
191 if (sta_id == IWL_MVM_STATION_COUNT) {
192 IWL_ERR(mvm,
193 "No station: Cannot switch SF to FULL_ON\n");
194 return -EINVAL;
195 }
196 rcu_read_lock();
197 sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
198 if (IS_ERR_OR_NULL(sta)) {
199 IWL_ERR(mvm, "Invalid station id\n");
200 rcu_read_unlock();
201 return -EINVAL;
202 }
203 iwl_mvm_fill_sf_command(&sf_cmd, sta);
204 rcu_read_unlock();
205 break;
206 case SF_INIT_OFF:
207 iwl_mvm_fill_sf_command(&sf_cmd, NULL);
208 break;
209 default:
210 WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
211 new_state);
212 return -EINVAL;
213 }
214
215 ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
216 sizeof(sf_cmd), &sf_cmd);
217 if (!ret)
218 mvm->sf_state = new_state;
219
220 return ret;
221}
222
223/*
224 * Update Smart fifo:
225 * Count bound interfaces that are not to be removed, ignoring p2p devices,
226 * and set new state accordingly.
227 */
228int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
229 bool remove_vif)
230{
231 enum iwl_sf_state new_state;
232 u8 sta_id = IWL_MVM_STATION_COUNT;
233 struct iwl_mvm_vif *mvmvif = NULL;
234 struct iwl_mvm_active_iface_iterator_data data = {
235 .ignore_vif = changed_vif,
236 .sta_vif_state = SF_UNINIT,
237 .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
238 };
239
240 if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
241 return 0;
242
243 /*
244 * Ignore the call if we are in HW Restart flow, or if the handled
245 * vif is a p2p device.
246 */
247 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
248 (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
249 return 0;
250
251 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
252 IEEE80211_IFACE_ITER_NORMAL,
253 iwl_mvm_bound_iface_iterator,
254 &data);
255
256 /* If changed_vif exists and is not to be removed, add to the count */
257 if (changed_vif && !remove_vif)
258 data.num_active_macs++;
259
260 switch (data.num_active_macs) {
261 case 0:
262 /* If there are no active macs - change state to SF_INIT_OFF */
263 new_state = SF_INIT_OFF;
264 break;
265 case 1:
266 if (remove_vif) {
267 /* The one active mac left is of type station
268 * and we filled the relevant data during iteration
269 */
270 new_state = data.sta_vif_state;
271 sta_id = data.sta_vif_ap_sta_id;
272 } else {
273 if (WARN_ON(!changed_vif))
274 return -EINVAL;
275 if (changed_vif->type != NL80211_IFTYPE_STATION) {
276 new_state = SF_UNINIT;
277 } else if (changed_vif->bss_conf.assoc) {
278 mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
279 sta_id = mvmvif->ap_sta_id;
280 new_state = SF_FULL_ON;
281 } else {
282 new_state = SF_INIT_OFF;
283 }
284 }
285 break;
286 default:
287 /* If there are multiple active macs - change to SF_UNINIT */
288 new_state = SF_UNINIT;
289 }
290 return iwl_mvm_sf_config(mvm, sta_id, new_state);
291}