]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/net/wireless/ath/ath9k/btcoex.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[mv-sheeva.git] / drivers / net / wireless / ath / ath9k / btcoex.c
1 /*
2  * Copyright (c) 2009 Atheros Communications Inc.
3  *
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.
7  *
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.
15  */
16
17 #include "ath9k.h"
18
19 static const struct ath_btcoex_config ath_bt_config = { 0, true, true,
20                         ATH_BT_COEX_MODE_SLOTTED, true, true, 2, 5, true };
21
22
23 /*
24  * Detects if there is any priority bt traffic
25  */
26 static void ath_detect_bt_priority(struct ath_softc *sc)
27 {
28         struct ath_btcoex_info *btinfo = &sc->btcoex_info;
29
30         if (ath9k_hw_gpio_get(sc->sc_ah, btinfo->btpriority_gpio))
31                 btinfo->bt_priority_cnt++;
32
33         if (time_after(jiffies, btinfo->bt_priority_time +
34                         msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
35                 if (btinfo->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
36                         DPRINTF(sc, ATH_DBG_BTCOEX,
37                                 "BT priority traffic detected");
38                         sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
39                 } else {
40                         sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
41                 }
42
43                 btinfo->bt_priority_cnt = 0;
44                 btinfo->bt_priority_time = jiffies;
45         }
46 }
47
48 /*
49  * Configures appropriate weight based on stomp type.
50  */
51 static void ath_btcoex_bt_stomp(struct ath_softc *sc,
52                                 struct ath_btcoex_info *btinfo,
53                                 int stomp_type)
54 {
55
56         switch (stomp_type) {
57         case ATH_BTCOEX_STOMP_ALL:
58                 ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
59                                       AR_STOMP_ALL_WLAN_WGHT);
60                 break;
61         case ATH_BTCOEX_STOMP_LOW:
62                 ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
63                                       AR_STOMP_LOW_WLAN_WGHT);
64                 break;
65         case ATH_BTCOEX_STOMP_NONE:
66                 ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
67                                       AR_STOMP_NONE_WLAN_WGHT);
68                 break;
69         default:
70                 DPRINTF(sc, ATH_DBG_BTCOEX, "Invalid Stomptype\n");
71                 break;
72         }
73
74         ath9k_hw_btcoex_enable(sc->sc_ah);
75 }
76
77 /*
78  * This is the master bt coex timer which runs for every
79  * 45ms, bt traffic will be given priority during 55% of this
80  * period while wlan gets remaining 45%
81  */
82
83 static void ath_btcoex_period_timer(unsigned long data)
84 {
85         struct ath_softc *sc = (struct ath_softc *) data;
86         struct ath_btcoex_info *btinfo = &sc->btcoex_info;
87         unsigned long flags;
88
89         ath_detect_bt_priority(sc);
90
91         spin_lock_irqsave(&btinfo->btcoex_lock, flags);
92
93         ath_btcoex_bt_stomp(sc, btinfo, btinfo->bt_stomp_type);
94
95         spin_unlock_irqrestore(&btinfo->btcoex_lock, flags);
96
97         if (btinfo->btcoex_period != btinfo->btcoex_no_stomp) {
98                 if (btinfo->hw_timer_enabled)
99                         ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
100
101                 ath_gen_timer_start(sc->sc_ah,
102                         btinfo->no_stomp_timer,
103                         (ath9k_hw_gettsf32(sc->sc_ah) +
104                                 btinfo->btcoex_no_stomp),
105                                 btinfo->btcoex_no_stomp * 10);
106                 btinfo->hw_timer_enabled = true;
107         }
108
109         mod_timer(&btinfo->period_timer, jiffies +
110                                   msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
111 }
112
113 /*
114  * Generic tsf based hw timer which configures weight
115  * registers to time slice between wlan and bt traffic
116  */
117
118 static void ath_btcoex_no_stomp_timer(void *arg)
119 {
120         struct ath_softc *sc = (struct ath_softc *)arg;
121         struct ath_btcoex_info *btinfo = &sc->btcoex_info;
122         unsigned long flags;
123
124         DPRINTF(sc, ATH_DBG_BTCOEX, "no stomp timer running \n");
125
126         spin_lock_irqsave(&btinfo->btcoex_lock, flags);
127
128         if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_LOW)
129                 ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_NONE);
130          else if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
131                 ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_LOW);
132
133         spin_unlock_irqrestore(&btinfo->btcoex_lock, flags);
134 }
135
136 static int ath_init_btcoex_info(struct ath_hw *hw,
137                                 struct ath_btcoex_info *btcoex_info)
138 {
139         u32 i;
140         int qnum;
141
142         qnum = ath_tx_get_qnum(hw->ah_sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
143
144         btcoex_info->bt_coex_mode =
145                 (btcoex_info->bt_coex_mode & AR_BT_QCU_THRESH) |
146                 SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) |
147                 SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) |
148                 SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) |
149                 SM(ath_bt_config.bt_mode, AR_BT_MODE) |
150                 SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) |
151                 SM(ath_bt_config.bt_rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) |
152                 SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) |
153                 SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) |
154                 SM(qnum, AR_BT_QCU_THRESH);
155
156         btcoex_info->bt_coex_mode2 =
157                 SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) |
158                 SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
159                 AR_BT_DISABLE_BT_ANT;
160
161         btcoex_info->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
162
163         btcoex_info->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
164
165         btcoex_info->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
166                 btcoex_info->btcoex_period / 100;
167
168         for (i = 0; i < 32; i++)
169                 hw->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i;
170
171         setup_timer(&btcoex_info->period_timer, ath_btcoex_period_timer,
172                         (unsigned long) hw->ah_sc);
173
174         btcoex_info->no_stomp_timer = ath_gen_timer_alloc(hw,
175                         ath_btcoex_no_stomp_timer,
176                         ath_btcoex_no_stomp_timer,
177                         (void *)hw->ah_sc, AR_FIRST_NDP_TIMER);
178
179         if (btcoex_info->no_stomp_timer == NULL)
180                 return -ENOMEM;
181
182         spin_lock_init(&btcoex_info->btcoex_lock);
183
184         return 0;
185 }
186
187 int ath9k_hw_btcoex_init(struct ath_hw *ah)
188 {
189         struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
190         int ret = 0;
191
192         if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) {
193                 /* connect bt_active to baseband */
194                 REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL,
195                                 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF |
196                                  AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF));
197
198                 REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
199                                 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
200
201                 /* Set input mux for bt_active to gpio pin */
202                 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
203                                 AR_GPIO_INPUT_MUX1_BT_ACTIVE,
204                                 btcoex_info->btactive_gpio);
205
206                 /* Configure the desired gpio port for input */
207                 ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
208         } else {
209                 /* btcoex 3-wire */
210                 REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
211                                 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB |
212                                  AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB));
213
214                 /* Set input mux for bt_prority_async and
215                  *                  bt_active_async to GPIO pins */
216                 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
217                                 AR_GPIO_INPUT_MUX1_BT_ACTIVE,
218                                 btcoex_info->btactive_gpio);
219
220                 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
221                                 AR_GPIO_INPUT_MUX1_BT_PRIORITY,
222                                 btcoex_info->btpriority_gpio);
223
224                 /* Configure the desired GPIO ports for input */
225
226                 ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
227                 ath9k_hw_cfg_gpio_input(ah, btcoex_info->btpriority_gpio);
228
229                 ret = ath_init_btcoex_info(ah, btcoex_info);
230         }
231
232         return ret;
233 }
234
235 void ath9k_hw_btcoex_enable(struct ath_hw *ah)
236 {
237         struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
238
239         if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) {
240                 /* Configure the desired GPIO port for TX_FRAME output */
241                 ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
242                                 AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
243         } else {
244                 /*
245                  * Program coex mode and weight registers to
246                  * enable coex 3-wire
247                  */
248                 REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_info->bt_coex_mode);
249                 REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_info->bt_coex_weights);
250                 REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_info->bt_coex_mode2);
251
252                 REG_RMW_FIELD(ah, AR_QUIET1,
253                                 AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
254                 REG_RMW_FIELD(ah, AR_PCU_MISC,
255                                 AR_PCU_BT_ANT_PREVENT_RX, 0);
256
257                 ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
258                                 AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL);
259         }
260
261         REG_RMW(ah, AR_GPIO_PDPU,
262                 (0x2 << (btcoex_info->btactive_gpio * 2)),
263                 (0x3 << (btcoex_info->btactive_gpio * 2)));
264
265         ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED;
266 }
267
268 void ath9k_hw_btcoex_disable(struct ath_hw *ah)
269 {
270         struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
271
272         ath9k_hw_set_gpio(ah, btcoex_info->wlanactive_gpio, 0);
273
274         ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
275                         AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
276
277         if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) {
278                 REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE);
279                 REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0);
280                 REG_WRITE(ah, AR_BT_COEX_MODE2, 0);
281         }
282
283         ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED;
284 }
285
286 /*
287  * Pause btcoex timer and bt duty cycle timer
288  */
289 void ath_btcoex_timer_pause(struct ath_softc *sc,
290                             struct ath_btcoex_info *btinfo)
291 {
292
293         del_timer_sync(&btinfo->period_timer);
294
295         if (btinfo->hw_timer_enabled)
296                 ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
297
298         btinfo->hw_timer_enabled = false;
299 }
300
301 /*
302  * (Re)start btcoex timers
303  */
304 void ath_btcoex_timer_resume(struct ath_softc *sc,
305                              struct ath_btcoex_info *btinfo)
306 {
307
308         DPRINTF(sc, ATH_DBG_BTCOEX, "Starting btcoex timers");
309
310         /* make sure duty cycle timer is also stopped when resuming */
311         if (btinfo->hw_timer_enabled)
312                 ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
313
314         btinfo->bt_priority_cnt = 0;
315         btinfo->bt_priority_time = jiffies;
316         sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
317
318         mod_timer(&btinfo->period_timer, jiffies);
319 }