aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm/coex.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-02 23:53:45 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-02 23:53:45 -0400
commitcd6362befe4cc7bf589a5236d2a780af2d47bcc9 (patch)
tree3bd4e13ec3f92a00dc4f6c3d65e820b54dbfe46e /drivers/net/wireless/iwlwifi/mvm/coex.c
parent0f1b1e6d73cb989ce2c071edc57deade3b084dfe (diff)
parentb1586f099ba897542ece36e8a23c1a62907261ef (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Here is my initial pull request for the networking subsystem during this merge window: 1) Support for ESN in AH (RFC 4302) from Fan Du. 2) Add full kernel doc for ethtool command structures, from Ben Hutchings. 3) Add BCM7xxx PHY driver, from Florian Fainelli. 4) Export computed TCP rate information in netlink socket dumps, from Eric Dumazet. 5) Allow IPSEC SA to be dumped partially using a filter, from Nicolas Dichtel. 6) Convert many drivers to pci_enable_msix_range(), from Alexander Gordeev. 7) Record SKB timestamps more efficiently, from Eric Dumazet. 8) Switch to microsecond resolution for TCP round trip times, also from Eric Dumazet. 9) Clean up and fix 6lowpan fragmentation handling by making use of the existing inet_frag api for it's implementation. 10) Add TX grant mapping to xen-netback driver, from Zoltan Kiss. 11) Auto size SKB lengths when composing netlink messages based upon past message sizes used, from Eric Dumazet. 12) qdisc dumps can take a long time, add a cond_resched(), From Eric Dumazet. 13) Sanitize netpoll core and drivers wrt. SKB handling semantics. Get rid of never-used-in-tree netpoll RX handling. From Eric W Biederman. 14) Support inter-address-family and namespace changing in VTI tunnel driver(s). From Steffen Klassert. 15) Add Altera TSE driver, from Vince Bridgers. 16) Optimizing csum_replace2() so that it doesn't adjust the checksum by checksumming the entire header, from Eric Dumazet. 17) Expand BPF internal implementation for faster interpreting, more direct translations into JIT'd code, and much cleaner uses of BPF filtering in non-socket ocntexts. From Daniel Borkmann and Alexei Starovoitov" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1976 commits) netpoll: Use skb_irq_freeable to make zap_completion_queue safe. net: Add a test to see if a skb is freeable in irq context qlcnic: Fix build failure due to undefined reference to `vxlan_get_rx_port' net: ptp: move PTP classifier in its own file net: sxgbe: make "core_ops" static net: sxgbe: fix logical vs bitwise operation net: sxgbe: sxgbe_mdio_register() frees the bus Call efx_set_channels() before efx->type->dimension_resources() xen-netback: disable rogue vif in kthread context net/mlx4: Set proper build dependancy with vxlan be2net: fix build dependency on VxLAN mac802154: make csma/cca parameters per-wpan mac802154: allow only one WPAN to be up at any given time net: filter: minor: fix kdoc in __sk_run_filter netlink: don't compare the nul-termination in nla_strcmp can: c_can: Avoid led toggling for every packet. can: c_can: Simplify TX interrupt cleanup can: c_can: Store dlc private can: c_can: Reduce register access can: c_can: Make the code readable ...
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/coex.c')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex.c1322
1 files changed, 1322 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
new file mode 100644
index 000000000000..685f7e8e6943
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/coex.c
@@ -0,0 +1,1322 @@
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 - 2014 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 - 2014 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
64#include <linux/ieee80211.h>
65#include <linux/etherdevice.h>
66#include <net/mac80211.h>
67
68#include "fw-api-coex.h"
69#include "iwl-modparams.h"
70#include "mvm.h"
71#include "iwl-debug.h"
72
73#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
74 [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
75 ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
76
77static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
78 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
79 BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
80 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
81 BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
82 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
83 BT_COEX_PRIO_TBL_PRIO_LOW, 0),
84 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
85 BT_COEX_PRIO_TBL_PRIO_LOW, 1),
86 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
87 BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
88 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
89 BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
90 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
91 BT_COEX_PRIO_TBL_DISABLED, 0),
92 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
93 BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
94 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
95 BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
96 EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
97 BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
98 0, 0, 0, 0, 0, 0,
99};
100
101#undef EVENT_PRIO_ANT
102
103#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
104#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
105#define BT_ANTENNA_COUPLING_THRESHOLD (30)
106
107int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
108{
109 if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
110 return 0;
111
112 return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
113 sizeof(struct iwl_bt_coex_prio_tbl_cmd),
114 &iwl_bt_prio_tbl);
115}
116
117const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
118 [BT_KILL_MSK_DEFAULT] = 0xffff0000,
119 [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
120 [BT_KILL_MSK_REDUCED_TXPOW] = 0,
121};
122
123const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
124 [BT_KILL_MSK_DEFAULT] = 0xffff0000,
125 [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
126 [BT_KILL_MSK_REDUCED_TXPOW] = 0,
127};
128
129static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
130 cpu_to_le32(0xf0f0f0f0),
131 cpu_to_le32(0xc0c0c0c0),
132 cpu_to_le32(0xfcfcfcfc),
133 cpu_to_le32(0xff00ff00),
134};
135
136static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
137 {
138 cpu_to_le32(0x40000000),
139 cpu_to_le32(0x00000000),
140 cpu_to_le32(0x44000000),
141 cpu_to_le32(0x00000000),
142 cpu_to_le32(0x40000000),
143 cpu_to_le32(0x00000000),
144 cpu_to_le32(0x44000000),
145 cpu_to_le32(0x00000000),
146 cpu_to_le32(0xc0004000),
147 cpu_to_le32(0xf0005000),
148 cpu_to_le32(0xc0004000),
149 cpu_to_le32(0xf0005000),
150 },
151 {
152 cpu_to_le32(0x40000000),
153 cpu_to_le32(0x00000000),
154 cpu_to_le32(0x44000000),
155 cpu_to_le32(0x00000000),
156 cpu_to_le32(0x40000000),
157 cpu_to_le32(0x00000000),
158 cpu_to_le32(0x44000000),
159 cpu_to_le32(0x00000000),
160 cpu_to_le32(0xc0004000),
161 cpu_to_le32(0xf0005000),
162 cpu_to_le32(0xc0004000),
163 cpu_to_le32(0xf0005000),
164 },
165 {
166 cpu_to_le32(0x40000000),
167 cpu_to_le32(0x00000000),
168 cpu_to_le32(0x44000000),
169 cpu_to_le32(0x00000000),
170 cpu_to_le32(0x40000000),
171 cpu_to_le32(0x00000000),
172 cpu_to_le32(0x44000000),
173 cpu_to_le32(0x00000000),
174 cpu_to_le32(0xc0004000),
175 cpu_to_le32(0xf0005000),
176 cpu_to_le32(0xc0004000),
177 cpu_to_le32(0xf0005000),
178 },
179};
180
181static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
182 {
183 /* Tight */
184 cpu_to_le32(0xaaaaaaaa),
185 cpu_to_le32(0xaaaaaaaa),
186 cpu_to_le32(0xaeaaaaaa),
187 cpu_to_le32(0xaaaaaaaa),
188 cpu_to_le32(0xcc00ff28),
189 cpu_to_le32(0x0000aaaa),
190 cpu_to_le32(0xcc00aaaa),
191 cpu_to_le32(0x0000aaaa),
192 cpu_to_le32(0xc0004000),
193 cpu_to_le32(0x00000000),
194 cpu_to_le32(0xf0005000),
195 cpu_to_le32(0xf0005000),
196 },
197 {
198 /* Loose */
199 cpu_to_le32(0xaaaaaaaa),
200 cpu_to_le32(0xaaaaaaaa),
201 cpu_to_le32(0xaaaaaaaa),
202 cpu_to_le32(0xaaaaaaaa),
203 cpu_to_le32(0xcc00ff28),
204 cpu_to_le32(0x0000aaaa),
205 cpu_to_le32(0xcc00aaaa),
206 cpu_to_le32(0x0000aaaa),
207 cpu_to_le32(0x00000000),
208 cpu_to_le32(0x00000000),
209 cpu_to_le32(0xf0005000),
210 cpu_to_le32(0xf0005000),
211 },
212 {
213 /* Tx Tx disabled */
214 cpu_to_le32(0xaaaaaaaa),
215 cpu_to_le32(0xaaaaaaaa),
216 cpu_to_le32(0xaaaaaaaa),
217 cpu_to_le32(0xaaaaaaaa),
218 cpu_to_le32(0xcc00ff28),
219 cpu_to_le32(0x0000aaaa),
220 cpu_to_le32(0xcc00aaaa),
221 cpu_to_le32(0x0000aaaa),
222 cpu_to_le32(0xC0004000),
223 cpu_to_le32(0xC0004000),
224 cpu_to_le32(0xF0005000),
225 cpu_to_le32(0xF0005000),
226 },
227};
228
229/* 20MHz / 40MHz below / 40Mhz above*/
230static const __le64 iwl_ci_mask[][3] = {
231 /* dummy entry for channel 0 */
232 {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
233 {
234 cpu_to_le64(0x0000001FFFULL),
235 cpu_to_le64(0x0ULL),
236 cpu_to_le64(0x00007FFFFFULL),
237 },
238 {
239 cpu_to_le64(0x000000FFFFULL),
240 cpu_to_le64(0x0ULL),
241 cpu_to_le64(0x0003FFFFFFULL),
242 },
243 {
244 cpu_to_le64(0x000003FFFCULL),
245 cpu_to_le64(0x0ULL),
246 cpu_to_le64(0x000FFFFFFCULL),
247 },
248 {
249 cpu_to_le64(0x00001FFFE0ULL),
250 cpu_to_le64(0x0ULL),
251 cpu_to_le64(0x007FFFFFE0ULL),
252 },
253 {
254 cpu_to_le64(0x00007FFF80ULL),
255 cpu_to_le64(0x00007FFFFFULL),
256 cpu_to_le64(0x01FFFFFF80ULL),
257 },
258 {
259 cpu_to_le64(0x0003FFFC00ULL),
260 cpu_to_le64(0x0003FFFFFFULL),
261 cpu_to_le64(0x0FFFFFFC00ULL),
262 },
263 {
264 cpu_to_le64(0x000FFFF000ULL),
265 cpu_to_le64(0x000FFFFFFCULL),
266 cpu_to_le64(0x3FFFFFF000ULL),
267 },
268 {
269 cpu_to_le64(0x007FFF8000ULL),
270 cpu_to_le64(0x007FFFFFE0ULL),
271 cpu_to_le64(0xFFFFFF8000ULL),
272 },
273 {
274 cpu_to_le64(0x01FFFE0000ULL),
275 cpu_to_le64(0x01FFFFFF80ULL),
276 cpu_to_le64(0xFFFFFE0000ULL),
277 },
278 {
279 cpu_to_le64(0x0FFFF00000ULL),
280 cpu_to_le64(0x0FFFFFFC00ULL),
281 cpu_to_le64(0x0ULL),
282 },
283 {
284 cpu_to_le64(0x3FFFC00000ULL),
285 cpu_to_le64(0x3FFFFFF000ULL),
286 cpu_to_le64(0x0)
287 },
288 {
289 cpu_to_le64(0xFFFE000000ULL),
290 cpu_to_le64(0xFFFFFF8000ULL),
291 cpu_to_le64(0x0)
292 },
293 {
294 cpu_to_le64(0xFFF8000000ULL),
295 cpu_to_le64(0xFFFFFE0000ULL),
296 cpu_to_le64(0x0)
297 },
298 {
299 cpu_to_le64(0xFFC0000000ULL),
300 cpu_to_le64(0x0ULL),
301 cpu_to_le64(0x0ULL)
302 },
303};
304
305static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
306 cpu_to_le32(0x22002200),
307 cpu_to_le32(0x33113311),
308};
309
310struct corunning_block_luts {
311 u8 range;
312 __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
313};
314
315/*
316 * Ranges for the antenna coupling calibration / co-running block LUT:
317 * LUT0: [ 0, 12[
318 * LUT1: [12, 20[
319 * LUT2: [20, 21[
320 * LUT3: [21, 23[
321 * LUT4: [23, 27[
322 * LUT5: [27, 30[
323 * LUT6: [30, 32[
324 * LUT7: [32, 33[
325 * LUT8: [33, - [
326 */
327static const struct corunning_block_luts antenna_coupling_ranges[] = {
328 {
329 .range = 0,
330 .lut20 = {
331 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
332 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
333 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
334 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
335 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
336 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
337 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
338 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
339 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
340 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
341 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
342 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
343 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
344 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
345 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
346 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
347 },
348 },
349 {
350 .range = 12,
351 .lut20 = {
352 cpu_to_le32(0x00000001), cpu_to_le32(0x00000000),
353 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
354 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
355 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
356 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
357 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
358 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
359 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
360 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
361 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
362 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
363 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
364 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
365 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
366 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
367 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
368 },
369 },
370 {
371 .range = 20,
372 .lut20 = {
373 cpu_to_le32(0x00000002), cpu_to_le32(0x00000000),
374 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
375 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
376 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
377 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
378 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
379 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
380 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
381 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
382 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
383 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
384 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
385 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
386 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
387 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
388 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
389 },
390 },
391 {
392 .range = 21,
393 .lut20 = {
394 cpu_to_le32(0x00000003), cpu_to_le32(0x00000000),
395 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
396 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
397 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
398 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
399 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
400 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
401 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
402 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
403 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
404 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
405 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
406 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
407 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
408 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
409 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
410 },
411 },
412 {
413 .range = 23,
414 .lut20 = {
415 cpu_to_le32(0x00000004), cpu_to_le32(0x00000000),
416 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
417 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
418 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
419 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
420 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
421 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
422 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
423 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
424 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
425 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
426 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
427 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
428 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
429 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
430 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
431 },
432 },
433 {
434 .range = 27,
435 .lut20 = {
436 cpu_to_le32(0x00000005), cpu_to_le32(0x00000000),
437 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
438 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
439 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
440 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
441 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
442 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
443 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
444 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
445 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
446 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
447 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
448 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
449 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
450 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
451 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
452 },
453 },
454 {
455 .range = 30,
456 .lut20 = {
457 cpu_to_le32(0x00000006), cpu_to_le32(0x00000000),
458 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
459 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
460 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
461 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
462 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
463 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
464 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
465 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
466 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
467 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
468 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
469 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
470 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
471 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
472 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
473 },
474 },
475 {
476 .range = 32,
477 .lut20 = {
478 cpu_to_le32(0x00000007), cpu_to_le32(0x00000000),
479 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
480 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
481 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
482 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
483 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
484 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
485 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
486 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
487 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
488 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
489 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
490 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
491 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
492 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
493 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
494 },
495 },
496 {
497 .range = 33,
498 .lut20 = {
499 cpu_to_le32(0x00000008), cpu_to_le32(0x00000000),
500 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
501 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
502 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
503 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
504 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
505 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
506 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
507 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
508 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
509 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
510 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
511 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
512 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
513 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
514 cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
515 },
516 },
517};
518
519static enum iwl_bt_coex_lut_type
520iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
521{
522 struct ieee80211_chanctx_conf *chanctx_conf;
523 enum iwl_bt_coex_lut_type ret;
524 u16 phy_ctx_id;
525
526 /*
527 * Checking that we hold mvm->mutex is a good idea, but the rate
528 * control can't acquire the mutex since it runs in Tx path.
529 * So this is racy in that case, but in the worst case, the AMPDU
530 * size limit will be wrong for a short time which is not a big
531 * issue.
532 */
533
534 rcu_read_lock();
535
536 chanctx_conf = rcu_dereference(vif->chanctx_conf);
537
538 if (!chanctx_conf ||
539 chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
540 rcu_read_unlock();
541 return BT_COEX_LOOSE_LUT;
542 }
543
544 ret = BT_COEX_TX_DIS_LUT;
545
546 if (mvm->cfg->bt_shared_single_ant) {
547 rcu_read_unlock();
548 return ret;
549 }
550
551 phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
552
553 if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
554 ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
555 else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
556 ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
557 /* else - default = TX TX disallowed */
558
559 rcu_read_unlock();
560
561 return ret;
562}
563
564int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
565{
566 struct iwl_bt_coex_cmd *bt_cmd;
567 struct iwl_host_cmd cmd = {
568 .id = BT_CONFIG,
569 .len = { sizeof(*bt_cmd), },
570 .dataflags = { IWL_HCMD_DFL_NOCOPY, },
571 .flags = CMD_SYNC,
572 };
573 int ret;
574 u32 flags;
575
576 if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
577 return 0;
578
579 bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
580 if (!bt_cmd)
581 return -ENOMEM;
582 cmd.data[0] = bt_cmd;
583
584 bt_cmd->max_kill = 5;
585 bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD,
586 bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling,
587 bt_cmd->bt4_tx_tx_delta_freq_thr = 15,
588 bt_cmd->bt4_tx_rx_max_freq0 = 15,
589
590 flags = iwlwifi_mod_params.bt_coex_active ?
591 BT_COEX_NW : BT_COEX_DISABLE;
592 bt_cmd->flags = cpu_to_le32(flags);
593
594 bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
595 BT_VALID_BT_PRIO_BOOST |
596 BT_VALID_MAX_KILL |
597 BT_VALID_3W_TMRS |
598 BT_VALID_KILL_ACK |
599 BT_VALID_KILL_CTS |
600 BT_VALID_REDUCED_TX_POWER |
601 BT_VALID_LUT |
602 BT_VALID_WIFI_RX_SW_PRIO_BOOST |
603 BT_VALID_WIFI_TX_SW_PRIO_BOOST |
604 BT_VALID_ANT_ISOLATION |
605 BT_VALID_ANT_ISOLATION_THRS |
606 BT_VALID_TXTX_DELTA_FREQ_THRS |
607 BT_VALID_TXRX_MAX_FREQ_0 |
608 BT_VALID_SYNC_TO_SCO);
609
610 if (IWL_MVM_BT_COEX_SYNC2SCO)
611 bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
612
613 if (IWL_MVM_BT_COEX_CORUNNING) {
614 bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 |
615 BT_VALID_CORUN_LUT_40);
616 bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
617 }
618
619 if (IWL_MVM_BT_COEX_MPLUT) {
620 bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
621 bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
622 }
623
624 if (mvm->cfg->bt_shared_single_ant)
625 memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
626 sizeof(iwl_single_shared_ant));
627 else
628 memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
629 sizeof(iwl_combined_lookup));
630
631 /* Take first Co-running block LUT to get started */
632 memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
633 sizeof(bt_cmd->bt4_corun_lut20));
634 memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
635 sizeof(bt_cmd->bt4_corun_lut40));
636
637 memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
638 sizeof(iwl_bt_prio_boost));
639 memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
640 sizeof(iwl_bt_mprio_lut));
641 bt_cmd->kill_ack_msk =
642 cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
643 bt_cmd->kill_cts_msk =
644 cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
645
646 memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
647 memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
648
649 ret = iwl_mvm_send_cmd(mvm, &cmd);
650
651 kfree(bt_cmd);
652 return ret;
653}
654
655static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
656 bool reduced_tx_power)
657{
658 enum iwl_bt_kill_msk bt_kill_msk;
659 struct iwl_bt_coex_cmd *bt_cmd;
660 struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
661 struct iwl_host_cmd cmd = {
662 .id = BT_CONFIG,
663 .data[0] = &bt_cmd,
664 .len = { sizeof(*bt_cmd), },
665 .dataflags = { IWL_HCMD_DFL_NOCOPY, },
666 .flags = CMD_SYNC,
667 };
668 int ret = 0;
669
670 lockdep_assert_held(&mvm->mutex);
671
672 if (reduced_tx_power) {
673 /* Reduced Tx power has precedence on the type of the profile */
674 bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
675 } else {
676 /* Low latency BT profile is active: give higher prio to BT */
677 if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
678 BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
679 BT_MBOX_MSG(notif, 3, SNIFF_STATE))
680 bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
681 else
682 bt_kill_msk = BT_KILL_MSK_DEFAULT;
683 }
684
685 IWL_DEBUG_COEX(mvm,
686 "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
687 bt_kill_msk,
688 BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
689 BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
690 BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
691
692 /* Don't send HCMD if there is no update */
693 if (bt_kill_msk == mvm->bt_kill_msk)
694 return 0;
695
696 mvm->bt_kill_msk = bt_kill_msk;
697
698 bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
699 if (!bt_cmd)
700 return -ENOMEM;
701 cmd.data[0] = bt_cmd;
702 bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
703
704 bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
705 bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
706 bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
707 BT_VALID_KILL_ACK |
708 BT_VALID_KILL_CTS);
709
710 IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
711 iwl_bt_ack_kill_msk[bt_kill_msk],
712 iwl_bt_cts_kill_msk[bt_kill_msk]);
713
714 ret = iwl_mvm_send_cmd(mvm, &cmd);
715
716 kfree(bt_cmd);
717 return ret;
718}
719
720int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
721{
722 struct iwl_bt_coex_cmd *bt_cmd;
723 /* Send ASYNC since this can be sent from an atomic context */
724 struct iwl_host_cmd cmd = {
725 .id = BT_CONFIG,
726 .len = { sizeof(*bt_cmd), },
727 .dataflags = { IWL_HCMD_DFL_NOCOPY, },
728 .flags = CMD_ASYNC,
729 };
730 struct iwl_mvm_sta *mvmsta;
731 int ret;
732
733 mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
734 if (!mvmsta)
735 return 0;
736
737 /* nothing to do */
738 if (mvmsta->bt_reduced_txpower_dbg ||
739 mvmsta->bt_reduced_txpower == enable)
740 return 0;
741
742 bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
743 if (!bt_cmd)
744 return -ENOMEM;
745 cmd.data[0] = bt_cmd;
746 bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
747
748 bt_cmd->valid_bit_msk =
749 cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
750 bt_cmd->bt_reduced_tx_power = sta_id;
751
752 if (enable)
753 bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
754
755 IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
756 enable ? "en" : "dis", sta_id);
757
758 mvmsta->bt_reduced_txpower = enable;
759
760 ret = iwl_mvm_send_cmd(mvm, &cmd);
761
762 kfree(bt_cmd);
763 return ret;
764}
765
766struct iwl_bt_iterator_data {
767 struct iwl_bt_coex_profile_notif *notif;
768 struct iwl_mvm *mvm;
769 u32 num_bss_ifaces;
770 bool reduced_tx_power;
771 struct ieee80211_chanctx_conf *primary;
772 struct ieee80211_chanctx_conf *secondary;
773 bool primary_ll;
774};
775
776static inline
777void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
778 struct ieee80211_vif *vif,
779 bool enable, int rssi)
780{
781 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
782
783 mvmvif->bf_data.last_bt_coex_event = rssi;
784 mvmvif->bf_data.bt_coex_max_thold =
785 enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0;
786 mvmvif->bf_data.bt_coex_min_thold =
787 enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0;
788}
789
790/* must be called under rcu_read_lock */
791static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
792 struct ieee80211_vif *vif)
793{
794 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
795 struct iwl_bt_iterator_data *data = _data;
796 struct iwl_mvm *mvm = data->mvm;
797 struct ieee80211_chanctx_conf *chanctx_conf;
798 enum ieee80211_smps_mode smps_mode;
799 u32 bt_activity_grading;
800 int ave_rssi;
801
802 lockdep_assert_held(&mvm->mutex);
803
804 switch (vif->type) {
805 case NL80211_IFTYPE_STATION:
806 /* default smps_mode for BSS / P2P client is AUTOMATIC */
807 smps_mode = IEEE80211_SMPS_AUTOMATIC;
808 data->num_bss_ifaces++;
809
810 /*
811 * Count unassoc BSSes, relax SMSP constraints
812 * and disable reduced Tx Power
813 */
814 if (!vif->bss_conf.assoc) {
815 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
816 smps_mode);
817 if (iwl_mvm_bt_coex_reduced_txp(mvm,
818 mvmvif->ap_sta_id,
819 false))
820 IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
821 return;
822 }
823 break;
824 case NL80211_IFTYPE_AP:
825 /* default smps_mode for AP / GO is OFF */
826 smps_mode = IEEE80211_SMPS_OFF;
827 if (!mvmvif->ap_ibss_active) {
828 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
829 smps_mode);
830 return;
831 }
832
833 /* the Ack / Cts kill mask must be default if AP / GO */
834 data->reduced_tx_power = false;
835 break;
836 default:
837 return;
838 }
839
840 chanctx_conf = rcu_dereference(vif->chanctx_conf);
841
842 /* If channel context is invalid or not on 2.4GHz .. */
843 if ((!chanctx_conf ||
844 chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
845 /* ... relax constraints and disable rssi events */
846 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
847 smps_mode);
848 if (vif->type == NL80211_IFTYPE_STATION)
849 iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
850 return;
851 }
852
853 bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
854 if (bt_activity_grading >= BT_HIGH_TRAFFIC)
855 smps_mode = IEEE80211_SMPS_STATIC;
856 else if (bt_activity_grading >= BT_LOW_TRAFFIC)
857 smps_mode = vif->type == NL80211_IFTYPE_AP ?
858 IEEE80211_SMPS_OFF :
859 IEEE80211_SMPS_DYNAMIC;
860 IWL_DEBUG_COEX(data->mvm,
861 "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
862 mvmvif->id, data->notif->bt_status, bt_activity_grading,
863 smps_mode);
864
865 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
866
867 /* low latency is always primary */
868 if (iwl_mvm_vif_low_latency(mvmvif)) {
869 data->primary_ll = true;
870
871 data->secondary = data->primary;
872 data->primary = chanctx_conf;
873 }
874
875 if (vif->type == NL80211_IFTYPE_AP) {
876 if (!mvmvif->ap_ibss_active)
877 return;
878
879 if (chanctx_conf == data->primary)
880 return;
881
882 if (!data->primary_ll) {
883 /*
884 * downgrade the current primary no matter what its
885 * type is.
886 */
887 data->secondary = data->primary;
888 data->primary = chanctx_conf;
889 } else {
890 /* there is low latency vif - we will be secondary */
891 data->secondary = chanctx_conf;
892 }
893 return;
894 }
895
896 /*
897 * STA / P2P Client, try to be primary if first vif. If we are in low
898 * latency mode, we are already in primary and just don't do much
899 */
900 if (!data->primary || data->primary == chanctx_conf)
901 data->primary = chanctx_conf;
902 else if (!data->secondary)
903 /* if secondary is not NULL, it might be a GO */
904 data->secondary = chanctx_conf;
905
906 /* don't reduce the Tx power if in loose scheme */
907 if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
908 mvm->cfg->bt_shared_single_ant) {
909 data->reduced_tx_power = false;
910 iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
911 return;
912 }
913
914 /* reduced Txpower only if BT is on, so ...*/
915 if (!data->notif->bt_status) {
916 /* ... cancel reduced Tx power ... */
917 if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
918 IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
919 data->reduced_tx_power = false;
920
921 /* ... and there is no need to get reports on RSSI any more. */
922 iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
923 return;
924 }
925
926 /* try to get the avg rssi from fw */
927 ave_rssi = mvmvif->bf_data.ave_beacon_signal;
928
929 /* if the RSSI isn't valid, fake it is very low */
930 if (!ave_rssi)
931 ave_rssi = -100;
932 if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
933 if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
934 IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
935
936 /*
937 * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
938 * BSS / P2P clients have rssi above threshold.
939 * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
940 * the iteration, if one interface's rssi isn't good enough,
941 * bt_kill_msk will be set to default values.
942 */
943 } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
944 if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
945 IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
946
947 /*
948 * One interface hasn't rssi above threshold, bt_kill_msk must
949 * be set to default values.
950 */
951 data->reduced_tx_power = false;
952 }
953
954 /* Begin to monitor the RSSI: it may influence the reduced Tx power */
955 iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
956}
957
958static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
959{
960 struct iwl_bt_iterator_data data = {
961 .mvm = mvm,
962 .notif = &mvm->last_bt_notif,
963 .reduced_tx_power = true,
964 };
965 struct iwl_bt_coex_ci_cmd cmd = {};
966 u8 ci_bw_idx;
967
968 rcu_read_lock();
969 ieee80211_iterate_active_interfaces_atomic(
970 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
971 iwl_mvm_bt_notif_iterator, &data);
972
973 if (data.primary) {
974 struct ieee80211_chanctx_conf *chan = data.primary;
975 if (WARN_ON(!chan->def.chan)) {
976 rcu_read_unlock();
977 return;
978 }
979
980 if (chan->def.width < NL80211_CHAN_WIDTH_40) {
981 ci_bw_idx = 0;
982 cmd.co_run_bw_primary = 0;
983 } else {
984 cmd.co_run_bw_primary = 1;
985 if (chan->def.center_freq1 >
986 chan->def.chan->center_freq)
987 ci_bw_idx = 2;
988 else
989 ci_bw_idx = 1;
990 }
991
992 cmd.bt_primary_ci =
993 iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
994 cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
995 }
996
997 if (data.secondary) {
998 struct ieee80211_chanctx_conf *chan = data.secondary;
999 if (WARN_ON(!data.secondary->def.chan)) {
1000 rcu_read_unlock();
1001 return;
1002 }
1003
1004 if (chan->def.width < NL80211_CHAN_WIDTH_40) {
1005 ci_bw_idx = 0;
1006 cmd.co_run_bw_secondary = 0;
1007 } else {
1008 cmd.co_run_bw_secondary = 1;
1009 if (chan->def.center_freq1 >
1010 chan->def.chan->center_freq)
1011 ci_bw_idx = 2;
1012 else
1013 ci_bw_idx = 1;
1014 }
1015
1016 cmd.bt_secondary_ci =
1017 iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
1018 cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
1019 }
1020
1021 rcu_read_unlock();
1022
1023 /* Don't spam the fw with the same command over and over */
1024 if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
1025 if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, CMD_SYNC,
1026 sizeof(cmd), &cmd))
1027 IWL_ERR(mvm, "Failed to send BT_CI cmd");
1028 memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
1029 }
1030
1031 /*
1032 * If there are no BSS / P2P client interfaces, reduced Tx Power is
1033 * irrelevant since it is based on the RSSI coming from the beacon.
1034 * Use BT_KILL_MSK_DEFAULT in that case.
1035 */
1036 data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
1037
1038 if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
1039 IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
1040}
1041
1042/* upon association, the fw will send in BT Coex notification */
1043int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
1044 struct iwl_rx_cmd_buffer *rxb,
1045 struct iwl_device_cmd *dev_cmd)
1046{
1047 struct iwl_rx_packet *pkt = rxb_addr(rxb);
1048 struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
1049
1050
1051 IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
1052 IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
1053 notif->bt_status ? "ON" : "OFF");
1054 IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
1055 IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
1056 IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
1057 le32_to_cpu(notif->primary_ch_lut));
1058 IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
1059 le32_to_cpu(notif->secondary_ch_lut));
1060 IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
1061 le32_to_cpu(notif->bt_activity_grading));
1062 IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
1063 notif->bt_agg_traffic_load);
1064
1065 /* remember this notification for future use: rssi fluctuations */
1066 memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
1067
1068 iwl_mvm_bt_coex_notif_handle(mvm);
1069
1070 /*
1071 * This is an async handler for a notification, returning anything other
1072 * than 0 doesn't make sense even if HCMD failed.
1073 */
1074 return 0;
1075}
1076
1077static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
1078 struct ieee80211_vif *vif)
1079{
1080 struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
1081 struct iwl_bt_iterator_data *data = _data;
1082 struct iwl_mvm *mvm = data->mvm;
1083
1084 struct ieee80211_sta *sta;
1085 struct iwl_mvm_sta *mvmsta;
1086
1087 struct ieee80211_chanctx_conf *chanctx_conf;
1088
1089 rcu_read_lock();
1090 chanctx_conf = rcu_dereference(vif->chanctx_conf);
1091 /* If channel context is invalid or not on 2.4GHz - don't count it */
1092 if (!chanctx_conf ||
1093 chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
1094 rcu_read_unlock();
1095 return;
1096 }
1097 rcu_read_unlock();
1098
1099 if (vif->type != NL80211_IFTYPE_STATION ||
1100 mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
1101 return;
1102
1103 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
1104 lockdep_is_held(&mvm->mutex));
1105
1106 /* This can happen if the station has been removed right now */
1107 if (IS_ERR_OR_NULL(sta))
1108 return;
1109
1110 mvmsta = iwl_mvm_sta_from_mac80211(sta);
1111
1112 data->num_bss_ifaces++;
1113
1114 /*
1115 * This interface doesn't support reduced Tx power (because of low
1116 * RSSI probably), then set bt_kill_msk to default values.
1117 */
1118 if (!mvmsta->bt_reduced_txpower)
1119 data->reduced_tx_power = false;
1120 /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
1121}
1122
1123void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1124 enum ieee80211_rssi_event rssi_event)
1125{
1126 struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
1127 struct iwl_bt_iterator_data data = {
1128 .mvm = mvm,
1129 .reduced_tx_power = true,
1130 };
1131 int ret;
1132
1133 lockdep_assert_held(&mvm->mutex);
1134
1135 /*
1136 * Rssi update while not associated - can happen since the statistics
1137 * are handled asynchronously
1138 */
1139 if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
1140 return;
1141
1142 /* No BT - reports should be disabled */
1143 if (!mvm->last_bt_notif.bt_status)
1144 return;
1145
1146 IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
1147 rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
1148
1149 /*
1150 * Check if rssi is good enough for reduced Tx power, but not in loose
1151 * scheme.
1152 */
1153 if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
1154 iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
1155 ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
1156 false);
1157 else
1158 ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
1159
1160 if (ret)
1161 IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
1162
1163 ieee80211_iterate_active_interfaces_atomic(
1164 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
1165 iwl_mvm_bt_rssi_iterator, &data);
1166
1167 /*
1168 * If there are no BSS / P2P client interfaces, reduced Tx Power is
1169 * irrelevant since it is based on the RSSI coming from the beacon.
1170 * Use BT_KILL_MSK_DEFAULT in that case.
1171 */
1172 data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
1173
1174 if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
1175 IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
1176}
1177
1178#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
1179#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
1180
1181u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
1182 struct ieee80211_sta *sta)
1183{
1184 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
1185 enum iwl_bt_coex_lut_type lut_type;
1186
1187 if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
1188 BT_HIGH_TRAFFIC)
1189 return LINK_QUAL_AGG_TIME_LIMIT_DEF;
1190
1191 lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
1192
1193 if (lut_type == BT_COEX_LOOSE_LUT)
1194 return LINK_QUAL_AGG_TIME_LIMIT_DEF;
1195
1196 /* tight coex, high bt traffic, reduce AGG time limit */
1197 return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
1198}
1199
1200bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
1201 struct ieee80211_sta *sta)
1202{
1203 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
1204
1205 if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
1206 BT_HIGH_TRAFFIC)
1207 return true;
1208
1209 /*
1210 * In Tight, BT can't Rx while we Tx, so use both antennas since BT is
1211 * already killed.
1212 * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we
1213 * Tx.
1214 */
1215 return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
1216}
1217
1218u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
1219 struct ieee80211_tx_info *info, u8 ac)
1220{
1221 __le16 fc = hdr->frame_control;
1222
1223 if (info->band != IEEE80211_BAND_2GHZ)
1224 return 0;
1225
1226 if (unlikely(mvm->bt_tx_prio))
1227 return mvm->bt_tx_prio - 1;
1228
1229 /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
1230 if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO ||
1231 is_multicast_ether_addr(hdr->addr1) ||
1232 ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) ||
1233 ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc))
1234 return 3;
1235
1236 switch (ac) {
1237 case IEEE80211_AC_BE:
1238 return 1;
1239 case IEEE80211_AC_VO:
1240 return 3;
1241 case IEEE80211_AC_VI:
1242 return 2;
1243 default:
1244 break;
1245 }
1246
1247 return 0;
1248}
1249
1250void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
1251{
1252 if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
1253 return;
1254
1255 iwl_mvm_bt_coex_notif_handle(mvm);
1256}
1257
1258int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
1259 struct iwl_rx_cmd_buffer *rxb,
1260 struct iwl_device_cmd *dev_cmd)
1261{
1262 struct iwl_rx_packet *pkt = rxb_addr(rxb);
1263 u32 ant_isolation = le32_to_cpup((void *)pkt->data);
1264 u8 __maybe_unused lower_bound, upper_bound;
1265 u8 lut;
1266
1267 struct iwl_bt_coex_cmd *bt_cmd;
1268 struct iwl_host_cmd cmd = {
1269 .id = BT_CONFIG,
1270 .len = { sizeof(*bt_cmd), },
1271 .dataflags = { IWL_HCMD_DFL_NOCOPY, },
1272 .flags = CMD_SYNC,
1273 };
1274
1275 if (!IWL_MVM_BT_COEX_CORUNNING)
1276 return 0;
1277
1278 lockdep_assert_held(&mvm->mutex);
1279
1280 if (ant_isolation == mvm->last_ant_isol)
1281 return 0;
1282
1283 for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
1284 if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
1285 break;
1286
1287 lower_bound = antenna_coupling_ranges[lut].range;
1288
1289 if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
1290 upper_bound = antenna_coupling_ranges[lut + 1].range;
1291 else
1292 upper_bound = antenna_coupling_ranges[lut].range;
1293
1294 IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
1295 ant_isolation, lower_bound, upper_bound, lut);
1296
1297 mvm->last_ant_isol = ant_isolation;
1298
1299 if (mvm->last_corun_lut == lut)
1300 return 0;
1301
1302 mvm->last_corun_lut = lut;
1303
1304 bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
1305 if (!bt_cmd)
1306 return 0;
1307 cmd.data[0] = bt_cmd;
1308
1309 bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
1310 bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
1311 BT_VALID_CORUN_LUT_20 |
1312 BT_VALID_CORUN_LUT_40);
1313
1314 /* For the moment, use the same LUT for 20GHz and 40GHz */
1315 memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
1316 sizeof(bt_cmd->bt4_corun_lut20));
1317
1318 memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
1319 sizeof(bt_cmd->bt4_corun_lut40));
1320
1321 return 0;
1322}