2 * Copyright (c) 2009 Atheros Communications Inc.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 static const struct ath_btcoex_config ath_bt_config
= { 0, true, true,
20 ATH_BT_COEX_MODE_SLOTTED
, true, true, 2, 5, true };
22 static const u16 ath_subsysid_tbl
[] = {
23 AR9280_COEX2WIRE_SUBSYSID
,
24 AT9285_COEX3WIRE_SA_SUBSYSID
,
25 AT9285_COEX3WIRE_DA_SUBSYSID
29 * Checks the subsystem id of the device to see if it
32 bool ath_btcoex_supported(u16 subsysid
)
39 for (i
= 0; i
< ARRAY_SIZE(ath_subsysid_tbl
); i
++)
40 if (subsysid
== ath_subsysid_tbl
[i
])
46 static void ath_btcoex_set_weight(struct ath_btcoex_info
*btcoex_info
,
50 btcoex_info
->bt_coex_weights
= SM(bt_weight
, AR_BTCOEX_BT_WGHT
) |
51 SM(wlan_weight
, AR_BTCOEX_WL_WGHT
);
54 void ath9k_hw_btcoex_init_weight(struct ath_hw
*ah
)
56 ath_btcoex_set_weight(&ah
->btcoex_info
, AR_BT_COEX_WGHT
,
57 AR_STOMP_LOW_WLAN_WGHT
);
61 * Detects if there is any priority bt traffic
63 static void ath_detect_bt_priority(struct ath_softc
*sc
)
65 struct ath_btcoex
*btcoex
= &sc
->btcoex
;
66 struct ath_hw
*ah
= sc
->sc_ah
;
68 if (ath9k_hw_gpio_get(sc
->sc_ah
, ah
->btcoex_info
.btpriority_gpio
))
69 btcoex
->bt_priority_cnt
++;
71 if (time_after(jiffies
, btcoex
->bt_priority_time
+
72 msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD
))) {
73 if (btcoex
->bt_priority_cnt
>= ATH_BT_CNT_THRESHOLD
) {
74 DPRINTF(sc
->sc_ah
, ATH_DBG_BTCOEX
,
75 "BT priority traffic detected");
76 sc
->sc_flags
|= SC_OP_BT_PRIORITY_DETECTED
;
78 sc
->sc_flags
&= ~SC_OP_BT_PRIORITY_DETECTED
;
81 btcoex
->bt_priority_cnt
= 0;
82 btcoex
->bt_priority_time
= jiffies
;
87 * Configures appropriate weight based on stomp type.
89 static void ath_btcoex_bt_stomp(struct ath_softc
*sc
,
90 struct ath_btcoex_info
*btinfo
,
95 case ATH_BTCOEX_STOMP_ALL
:
96 ath_btcoex_set_weight(btinfo
, AR_BT_COEX_WGHT
,
97 AR_STOMP_ALL_WLAN_WGHT
);
99 case ATH_BTCOEX_STOMP_LOW
:
100 ath_btcoex_set_weight(btinfo
, AR_BT_COEX_WGHT
,
101 AR_STOMP_LOW_WLAN_WGHT
);
103 case ATH_BTCOEX_STOMP_NONE
:
104 ath_btcoex_set_weight(btinfo
, AR_BT_COEX_WGHT
,
105 AR_STOMP_NONE_WLAN_WGHT
);
108 DPRINTF(sc
->sc_ah
, ATH_DBG_BTCOEX
, "Invalid Stomptype\n");
112 ath9k_hw_btcoex_enable(sc
->sc_ah
);
116 * This is the master bt coex timer which runs for every
117 * 45ms, bt traffic will be given priority during 55% of this
118 * period while wlan gets remaining 45%
121 static void ath_btcoex_period_timer(unsigned long data
)
123 struct ath_softc
*sc
= (struct ath_softc
*) data
;
124 struct ath_hw
*ah
= sc
->sc_ah
;
125 struct ath_btcoex
*btcoex
= &sc
->btcoex
;
126 struct ath_btcoex_info
*btinfo
= &ah
->btcoex_info
;
128 ath_detect_bt_priority(sc
);
130 spin_lock_bh(&btcoex
->btcoex_lock
);
132 ath_btcoex_bt_stomp(sc
, btinfo
, btinfo
->bt_stomp_type
);
134 spin_unlock_bh(&btcoex
->btcoex_lock
);
136 if (btcoex
->btcoex_period
!= btcoex
->btcoex_no_stomp
) {
137 if (btcoex
->hw_timer_enabled
)
138 ath_gen_timer_stop(ah
, btinfo
->no_stomp_timer
);
140 ath_gen_timer_start(ah
,
141 btinfo
->no_stomp_timer
,
142 (ath9k_hw_gettsf32(sc
->sc_ah
) +
143 btcoex
->btcoex_no_stomp
),
144 btcoex
->btcoex_no_stomp
* 10);
145 btcoex
->hw_timer_enabled
= true;
148 mod_timer(&btcoex
->period_timer
, jiffies
+
149 msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD
));
153 * Generic tsf based hw timer which configures weight
154 * registers to time slice between wlan and bt traffic
157 static void ath_btcoex_no_stomp_timer(void *arg
)
159 struct ath_softc
*sc
= (struct ath_softc
*)arg
;
160 struct ath_hw
*ah
= sc
->sc_ah
;
161 struct ath_btcoex
*btcoex
= &sc
->btcoex
;
162 struct ath_btcoex_info
*btinfo
= &ah
->btcoex_info
;
164 DPRINTF(ah
, ATH_DBG_BTCOEX
, "no stomp timer running \n");
166 spin_lock_bh(&btcoex
->btcoex_lock
);
168 if (btinfo
->bt_stomp_type
== ATH_BTCOEX_STOMP_LOW
)
169 ath_btcoex_bt_stomp(sc
, btinfo
, ATH_BTCOEX_STOMP_NONE
);
170 else if (btinfo
->bt_stomp_type
== ATH_BTCOEX_STOMP_ALL
)
171 ath_btcoex_bt_stomp(sc
, btinfo
, ATH_BTCOEX_STOMP_LOW
);
173 spin_unlock_bh(&btcoex
->btcoex_lock
);
176 static int ath_init_btcoex_info(struct ath_hw
*ah
,
177 struct ath_btcoex_info
*btcoex_info
)
179 struct ath_btcoex
*btcoex
= &ah
->ah_sc
->btcoex
;
183 qnum
= ath_tx_get_qnum(ah
->ah_sc
, ATH9K_TX_QUEUE_DATA
, ATH9K_WME_AC_BE
);
185 btcoex_info
->bt_coex_mode
=
186 (btcoex_info
->bt_coex_mode
& AR_BT_QCU_THRESH
) |
187 SM(ath_bt_config
.bt_time_extend
, AR_BT_TIME_EXTEND
) |
188 SM(ath_bt_config
.bt_txstate_extend
, AR_BT_TXSTATE_EXTEND
) |
189 SM(ath_bt_config
.bt_txframe_extend
, AR_BT_TX_FRAME_EXTEND
) |
190 SM(ath_bt_config
.bt_mode
, AR_BT_MODE
) |
191 SM(ath_bt_config
.bt_quiet_collision
, AR_BT_QUIET
) |
192 SM(ath_bt_config
.bt_rxclear_polarity
, AR_BT_RX_CLEAR_POLARITY
) |
193 SM(ath_bt_config
.bt_priority_time
, AR_BT_PRIORITY_TIME
) |
194 SM(ath_bt_config
.bt_first_slot_time
, AR_BT_FIRST_SLOT_TIME
) |
195 SM(qnum
, AR_BT_QCU_THRESH
);
197 btcoex_info
->bt_coex_mode2
=
198 SM(ath_bt_config
.bt_hold_rx_clear
, AR_BT_HOLD_RX_CLEAR
) |
199 SM(ATH_BTCOEX_BMISS_THRESH
, AR_BT_BCN_MISS_THRESH
) |
200 AR_BT_DISABLE_BT_ANT
;
202 btcoex_info
->bt_stomp_type
= ATH_BTCOEX_STOMP_LOW
;
204 btcoex
->btcoex_period
= ATH_BTCOEX_DEF_BT_PERIOD
* 1000;
206 btcoex
->btcoex_no_stomp
= (100 - ATH_BTCOEX_DEF_DUTY_CYCLE
) *
207 btcoex
->btcoex_period
/ 100;
209 for (i
= 0; i
< 32; i
++)
210 ah
->hw_gen_timers
.gen_timer_index
[(debruijn32
<< i
) >> 27] = i
;
212 setup_timer(&btcoex
->period_timer
, ath_btcoex_period_timer
,
213 (unsigned long) ah
->ah_sc
);
215 btcoex_info
->no_stomp_timer
= ath_gen_timer_alloc(ah
,
216 ath_btcoex_no_stomp_timer
,
217 ath_btcoex_no_stomp_timer
,
218 (void *)ah
->ah_sc
, AR_FIRST_NDP_TIMER
);
220 if (btcoex_info
->no_stomp_timer
== NULL
)
223 spin_lock_init(&btcoex
->btcoex_lock
);
228 int ath9k_hw_btcoex_init(struct ath_hw
*ah
)
230 struct ath_btcoex_info
*btcoex_info
= &ah
->btcoex_info
;
233 if (btcoex_info
->btcoex_scheme
== ATH_BTCOEX_CFG_2WIRE
) {
234 /* connect bt_active to baseband */
235 REG_CLR_BIT(ah
, AR_GPIO_INPUT_EN_VAL
,
236 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF
|
237 AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF
));
239 REG_SET_BIT(ah
, AR_GPIO_INPUT_EN_VAL
,
240 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB
);
242 /* Set input mux for bt_active to gpio pin */
243 REG_RMW_FIELD(ah
, AR_GPIO_INPUT_MUX1
,
244 AR_GPIO_INPUT_MUX1_BT_ACTIVE
,
245 btcoex_info
->btactive_gpio
);
247 /* Configure the desired gpio port for input */
248 ath9k_hw_cfg_gpio_input(ah
, btcoex_info
->btactive_gpio
);
251 REG_SET_BIT(ah
, AR_GPIO_INPUT_EN_VAL
,
252 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB
|
253 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB
));
255 /* Set input mux for bt_prority_async and
256 * bt_active_async to GPIO pins */
257 REG_RMW_FIELD(ah
, AR_GPIO_INPUT_MUX1
,
258 AR_GPIO_INPUT_MUX1_BT_ACTIVE
,
259 btcoex_info
->btactive_gpio
);
261 REG_RMW_FIELD(ah
, AR_GPIO_INPUT_MUX1
,
262 AR_GPIO_INPUT_MUX1_BT_PRIORITY
,
263 btcoex_info
->btpriority_gpio
);
265 /* Configure the desired GPIO ports for input */
267 ath9k_hw_cfg_gpio_input(ah
, btcoex_info
->btactive_gpio
);
268 ath9k_hw_cfg_gpio_input(ah
, btcoex_info
->btpriority_gpio
);
270 ret
= ath_init_btcoex_info(ah
, btcoex_info
);
276 void ath9k_hw_btcoex_enable(struct ath_hw
*ah
)
278 struct ath_btcoex_info
*btcoex_info
= &ah
->btcoex_info
;
280 if (btcoex_info
->btcoex_scheme
== ATH_BTCOEX_CFG_2WIRE
) {
281 /* Configure the desired GPIO port for TX_FRAME output */
282 ath9k_hw_cfg_output(ah
, btcoex_info
->wlanactive_gpio
,
283 AR_GPIO_OUTPUT_MUX_AS_TX_FRAME
);
286 * Program coex mode and weight registers to
289 REG_WRITE(ah
, AR_BT_COEX_MODE
, btcoex_info
->bt_coex_mode
);
290 REG_WRITE(ah
, AR_BT_COEX_WEIGHT
, btcoex_info
->bt_coex_weights
);
291 REG_WRITE(ah
, AR_BT_COEX_MODE2
, btcoex_info
->bt_coex_mode2
);
293 REG_RMW_FIELD(ah
, AR_QUIET1
,
294 AR_QUIET1_QUIET_ACK_CTS_ENABLE
, 1);
295 REG_RMW_FIELD(ah
, AR_PCU_MISC
,
296 AR_PCU_BT_ANT_PREVENT_RX
, 0);
298 ath9k_hw_cfg_output(ah
, btcoex_info
->wlanactive_gpio
,
299 AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL
);
302 REG_RMW(ah
, AR_GPIO_PDPU
,
303 (0x2 << (btcoex_info
->btactive_gpio
* 2)),
304 (0x3 << (btcoex_info
->btactive_gpio
* 2)));
306 ah
->ah_sc
->sc_flags
|= SC_OP_BTCOEX_ENABLED
;
309 void ath9k_hw_btcoex_disable(struct ath_hw
*ah
)
311 struct ath_btcoex_info
*btcoex_info
= &ah
->btcoex_info
;
313 ath9k_hw_set_gpio(ah
, btcoex_info
->wlanactive_gpio
, 0);
315 ath9k_hw_cfg_output(ah
, btcoex_info
->wlanactive_gpio
,
316 AR_GPIO_OUTPUT_MUX_AS_OUTPUT
);
318 if (btcoex_info
->btcoex_scheme
== ATH_BTCOEX_CFG_3WIRE
) {
319 REG_WRITE(ah
, AR_BT_COEX_MODE
, AR_BT_QUIET
| AR_BT_MODE
);
320 REG_WRITE(ah
, AR_BT_COEX_WEIGHT
, 0);
321 REG_WRITE(ah
, AR_BT_COEX_MODE2
, 0);
324 ah
->ah_sc
->sc_flags
&= ~SC_OP_BTCOEX_ENABLED
;
328 * Pause btcoex timer and bt duty cycle timer
330 void ath_btcoex_timer_pause(struct ath_softc
*sc
)
332 struct ath_btcoex
*btcoex
= &sc
->btcoex
;
333 struct ath_hw
*ah
= sc
->sc_ah
;
335 del_timer_sync(&btcoex
->period_timer
);
337 if (btcoex
->hw_timer_enabled
)
338 ath_gen_timer_stop(ah
, ah
->btcoex_info
.no_stomp_timer
);
340 btcoex
->hw_timer_enabled
= false;
344 * (Re)start btcoex timers
346 void ath_btcoex_timer_resume(struct ath_softc
*sc
)
348 struct ath_btcoex
*btcoex
= &sc
->btcoex
;
349 struct ath_hw
*ah
= sc
->sc_ah
;
351 DPRINTF(ah
, ATH_DBG_BTCOEX
, "Starting btcoex timers");
353 /* make sure duty cycle timer is also stopped when resuming */
354 if (btcoex
->hw_timer_enabled
)
355 ath_gen_timer_stop(sc
->sc_ah
, ah
->btcoex_info
.no_stomp_timer
);
357 btcoex
->bt_priority_cnt
= 0;
358 btcoex
->bt_priority_time
= jiffies
;
359 sc
->sc_flags
&= ~SC_OP_BT_PRIORITY_DETECTED
;
361 mod_timer(&btcoex
->period_timer
, jiffies
);