diff options
author | Vasanthakumar Thiagarajan <vasanth@atheros.com> | 2009-08-26 11:38:50 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-28 14:40:51 -0400 |
commit | 1773912bd25196c2a3ca6c174574561363f43b2b (patch) | |
tree | 9e48b824bb5800f90c59bfa2a9a4d6437f230e2f /drivers/net/wireless/ath/ath9k/btcoex.c | |
parent | ff155a45cea56ad7a90c3f5192db59a4c7812fde (diff) |
ath9k: Add Bluetooth Coexistence 3-wire support
This patch adds 3-wire bluetooth coex support for AR9285.
This support can be enabled through btcoex_enable modparam.
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/btcoex.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/btcoex.c | 290 |
1 files changed, 274 insertions, 16 deletions
diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 9f19cd1c1bef..8fb356748823 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c | |||
@@ -16,34 +16,251 @@ | |||
16 | 16 | ||
17 | #include "ath9k.h" | 17 | #include "ath9k.h" |
18 | 18 | ||
19 | void ath9k_hw_btcoex_init(struct ath_hw *ah) | 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) | ||
20 | { | 188 | { |
21 | struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; | 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); | ||
22 | 205 | ||
23 | /* connect bt_active to baseband */ | 206 | /* Configure the desired gpio port for input */ |
24 | REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, | 207 | ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); |
25 | (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | | 208 | } else { |
26 | AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); | 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)); | ||
27 | 213 | ||
28 | REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, | 214 | /* Set input mux for bt_prority_async and |
29 | AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); | 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); | ||
30 | 219 | ||
31 | /* Set input mux for bt_active to gpio pin */ | 220 | REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, |
32 | REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, | 221 | AR_GPIO_INPUT_MUX1_BT_PRIORITY, |
33 | AR_GPIO_INPUT_MUX1_BT_ACTIVE, | 222 | btcoex_info->btpriority_gpio); |
34 | btcoex_info->btactive_gpio); | ||
35 | 223 | ||
36 | /* Configure the desired gpio port for input */ | 224 | /* Configure the desired GPIO ports for input */ |
37 | ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); | 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; | ||
38 | } | 233 | } |
39 | 234 | ||
40 | void ath9k_hw_btcoex_enable(struct ath_hw *ah) | 235 | void ath9k_hw_btcoex_enable(struct ath_hw *ah) |
41 | { | 236 | { |
42 | struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; | 237 | struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; |
43 | 238 | ||
44 | /* Configure the desired GPIO port for TX_FRAME output */ | 239 | if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { |
45 | ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, | 240 | /* Configure the desired GPIO port for TX_FRAME output */ |
46 | AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); | 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))); | ||
47 | 264 | ||
48 | ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED; | 265 | ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED; |
49 | } | 266 | } |
@@ -57,5 +274,46 @@ void ath9k_hw_btcoex_disable(struct ath_hw *ah) | |||
57 | ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, | 274 | ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, |
58 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | 275 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); |
59 | 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 | |||
60 | ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED; | 283 | ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED; |
61 | } | 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 | } | ||