Commit | Line | Data |
---|---|---|
1e1f4ad2 SM |
1 | /* |
2 | * Copyright (c) 2010 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 | ||
21cb9879 VN |
17 | #include "htc.h" |
18 | ||
19 | /******************/ | |
20 | /* BTCOEX */ | |
21 | /******************/ | |
22 | ||
23 | /* | |
24 | * Detects if there is any priority bt traffic | |
25 | */ | |
26 | static void ath_detect_bt_priority(struct ath9k_htc_priv *priv) | |
27 | { | |
28 | struct ath_btcoex *btcoex = &priv->btcoex; | |
29 | struct ath_hw *ah = priv->ah; | |
30 | ||
31 | if (ath9k_hw_gpio_get(ah, ah->btcoex_hw.btpriority_gpio)) | |
32 | btcoex->bt_priority_cnt++; | |
33 | ||
34 | if (time_after(jiffies, btcoex->bt_priority_time + | |
35 | msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { | |
36 | priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); | |
37 | /* Detect if colocated bt started scanning */ | |
38 | if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { | |
226afe68 JP |
39 | ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, |
40 | "BT scan detected\n"); | |
21cb9879 VN |
41 | priv->op_flags |= (OP_BT_SCAN | |
42 | OP_BT_PRIORITY_DETECTED); | |
43 | } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { | |
226afe68 JP |
44 | ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, |
45 | "BT priority traffic detected\n"); | |
21cb9879 VN |
46 | priv->op_flags |= OP_BT_PRIORITY_DETECTED; |
47 | } | |
48 | ||
49 | btcoex->bt_priority_cnt = 0; | |
50 | btcoex->bt_priority_time = jiffies; | |
51 | } | |
52 | } | |
53 | ||
54 | /* | |
55 | * This is the master bt coex work which runs for every | |
56 | * 45ms, bt traffic will be given priority during 55% of this | |
57 | * period while wlan gets remaining 45% | |
58 | */ | |
59 | static void ath_btcoex_period_work(struct work_struct *work) | |
60 | { | |
61 | struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, | |
62 | coex_period_work.work); | |
63 | struct ath_btcoex *btcoex = &priv->btcoex; | |
64 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
65 | u32 timer_period; | |
66 | bool is_btscan; | |
67 | int ret; | |
68 | u8 cmd_rsp, aggr; | |
69 | ||
70 | ath_detect_bt_priority(priv); | |
71 | ||
72 | is_btscan = !!(priv->op_flags & OP_BT_SCAN); | |
73 | ||
74 | aggr = priv->op_flags & OP_BT_PRIORITY_DETECTED; | |
75 | ||
76 | WMI_CMD_BUF(WMI_AGGR_LIMIT_CMD, &aggr); | |
77 | ||
78 | ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL : | |
79 | btcoex->bt_stomp_type); | |
80 | ||
81 | timer_period = is_btscan ? btcoex->btscan_no_stomp : | |
82 | btcoex->btcoex_no_stomp; | |
83 | ieee80211_queue_delayed_work(priv->hw, &priv->duty_cycle_work, | |
84 | msecs_to_jiffies(timer_period)); | |
85 | ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, | |
86 | msecs_to_jiffies(btcoex->btcoex_period)); | |
87 | } | |
88 | ||
89 | /* | |
90 | * Work to time slice between wlan and bt traffic and | |
91 | * configure weight registers | |
92 | */ | |
93 | static void ath_btcoex_duty_cycle_work(struct work_struct *work) | |
94 | { | |
95 | struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, | |
96 | duty_cycle_work.work); | |
97 | struct ath_hw *ah = priv->ah; | |
98 | struct ath_btcoex *btcoex = &priv->btcoex; | |
99 | struct ath_common *common = ath9k_hw_common(ah); | |
100 | bool is_btscan = priv->op_flags & OP_BT_SCAN; | |
101 | ||
226afe68 JP |
102 | ath_dbg(common, ATH_DBG_BTCOEX, |
103 | "time slice work for bt and wlan\n"); | |
21cb9879 VN |
104 | |
105 | if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) | |
106 | ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE); | |
107 | else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) | |
108 | ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW); | |
109 | } | |
110 | ||
111 | void ath_htc_init_btcoex_work(struct ath9k_htc_priv *priv) | |
112 | { | |
113 | struct ath_btcoex *btcoex = &priv->btcoex; | |
114 | ||
115 | btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; | |
116 | btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * | |
117 | btcoex->btcoex_period / 100; | |
118 | btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * | |
119 | btcoex->btcoex_period / 100; | |
120 | INIT_DELAYED_WORK(&priv->coex_period_work, ath_btcoex_period_work); | |
121 | INIT_DELAYED_WORK(&priv->duty_cycle_work, ath_btcoex_duty_cycle_work); | |
122 | } | |
123 | ||
124 | /* | |
125 | * (Re)start btcoex work | |
126 | */ | |
127 | ||
128 | void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) | |
129 | { | |
130 | struct ath_btcoex *btcoex = &priv->btcoex; | |
131 | struct ath_hw *ah = priv->ah; | |
132 | ||
226afe68 | 133 | ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX, "Starting btcoex work\n"); |
21cb9879 VN |
134 | |
135 | btcoex->bt_priority_cnt = 0; | |
136 | btcoex->bt_priority_time = jiffies; | |
137 | priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); | |
138 | ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, 0); | |
139 | } | |
140 | ||
141 | ||
142 | /* | |
143 | * Cancel btcoex and bt duty cycle work. | |
144 | */ | |
145 | void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv) | |
146 | { | |
147 | cancel_delayed_work_sync(&priv->coex_period_work); | |
148 | cancel_delayed_work_sync(&priv->duty_cycle_work); | |
149 | } | |
1e1f4ad2 SM |
150 | |
151 | /*******/ | |
152 | /* LED */ | |
153 | /*******/ | |
154 | ||
155 | static void ath9k_led_blink_work(struct work_struct *work) | |
156 | { | |
157 | struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, | |
158 | ath9k_led_blink_work.work); | |
159 | ||
160 | if (!(priv->op_flags & OP_LED_ASSOCIATED)) | |
161 | return; | |
162 | ||
163 | if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) || | |
164 | (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) | |
165 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); | |
166 | else | |
167 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, | |
168 | (priv->op_flags & OP_LED_ON) ? 1 : 0); | |
169 | ||
170 | ieee80211_queue_delayed_work(priv->hw, | |
171 | &priv->ath9k_led_blink_work, | |
172 | (priv->op_flags & OP_LED_ON) ? | |
173 | msecs_to_jiffies(priv->led_off_duration) : | |
174 | msecs_to_jiffies(priv->led_on_duration)); | |
175 | ||
176 | priv->led_on_duration = priv->led_on_cnt ? | |
177 | max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) : | |
178 | ATH_LED_ON_DURATION_IDLE; | |
179 | priv->led_off_duration = priv->led_off_cnt ? | |
180 | max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) : | |
181 | ATH_LED_OFF_DURATION_IDLE; | |
182 | priv->led_on_cnt = priv->led_off_cnt = 0; | |
183 | ||
184 | if (priv->op_flags & OP_LED_ON) | |
185 | priv->op_flags &= ~OP_LED_ON; | |
186 | else | |
187 | priv->op_flags |= OP_LED_ON; | |
188 | } | |
189 | ||
190 | static void ath9k_led_brightness_work(struct work_struct *work) | |
191 | { | |
192 | struct ath_led *led = container_of(work, struct ath_led, | |
193 | brightness_work.work); | |
194 | struct ath9k_htc_priv *priv = led->priv; | |
195 | ||
196 | switch (led->brightness) { | |
197 | case LED_OFF: | |
198 | if (led->led_type == ATH_LED_ASSOC || | |
199 | led->led_type == ATH_LED_RADIO) { | |
200 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, | |
201 | (led->led_type == ATH_LED_RADIO)); | |
202 | priv->op_flags &= ~OP_LED_ASSOCIATED; | |
203 | if (led->led_type == ATH_LED_RADIO) | |
204 | priv->op_flags &= ~OP_LED_ON; | |
205 | } else { | |
206 | priv->led_off_cnt++; | |
207 | } | |
208 | break; | |
209 | case LED_FULL: | |
210 | if (led->led_type == ATH_LED_ASSOC) { | |
211 | priv->op_flags |= OP_LED_ASSOCIATED; | |
212 | ieee80211_queue_delayed_work(priv->hw, | |
213 | &priv->ath9k_led_blink_work, 0); | |
214 | } else if (led->led_type == ATH_LED_RADIO) { | |
215 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); | |
216 | priv->op_flags |= OP_LED_ON; | |
217 | } else { | |
218 | priv->led_on_cnt++; | |
219 | } | |
220 | break; | |
221 | default: | |
222 | break; | |
223 | } | |
224 | } | |
225 | ||
226 | static void ath9k_led_brightness(struct led_classdev *led_cdev, | |
227 | enum led_brightness brightness) | |
228 | { | |
229 | struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); | |
230 | struct ath9k_htc_priv *priv = led->priv; | |
231 | ||
232 | led->brightness = brightness; | |
233 | if (!(priv->op_flags & OP_LED_DEINIT)) | |
234 | ieee80211_queue_delayed_work(priv->hw, | |
235 | &led->brightness_work, 0); | |
236 | } | |
237 | ||
238 | void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv) | |
239 | { | |
240 | cancel_delayed_work_sync(&priv->radio_led.brightness_work); | |
241 | cancel_delayed_work_sync(&priv->assoc_led.brightness_work); | |
242 | cancel_delayed_work_sync(&priv->tx_led.brightness_work); | |
243 | cancel_delayed_work_sync(&priv->rx_led.brightness_work); | |
244 | } | |
245 | ||
246 | static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led, | |
247 | char *trigger) | |
248 | { | |
249 | int ret; | |
250 | ||
251 | led->priv = priv; | |
252 | led->led_cdev.name = led->name; | |
253 | led->led_cdev.default_trigger = trigger; | |
254 | led->led_cdev.brightness_set = ath9k_led_brightness; | |
255 | ||
256 | ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev); | |
257 | if (ret) | |
258 | ath_err(ath9k_hw_common(priv->ah), | |
259 | "Failed to register led:%s", led->name); | |
260 | else | |
261 | led->registered = 1; | |
262 | ||
263 | INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work); | |
264 | ||
265 | return ret; | |
266 | } | |
267 | ||
268 | static void ath9k_unregister_led(struct ath_led *led) | |
269 | { | |
270 | if (led->registered) { | |
271 | led_classdev_unregister(&led->led_cdev); | |
272 | led->registered = 0; | |
273 | } | |
274 | } | |
275 | ||
276 | void ath9k_deinit_leds(struct ath9k_htc_priv *priv) | |
277 | { | |
278 | priv->op_flags |= OP_LED_DEINIT; | |
279 | ath9k_unregister_led(&priv->assoc_led); | |
280 | priv->op_flags &= ~OP_LED_ASSOCIATED; | |
281 | ath9k_unregister_led(&priv->tx_led); | |
282 | ath9k_unregister_led(&priv->rx_led); | |
283 | ath9k_unregister_led(&priv->radio_led); | |
284 | } | |
285 | ||
286 | void ath9k_init_leds(struct ath9k_htc_priv *priv) | |
287 | { | |
288 | char *trigger; | |
289 | int ret; | |
290 | ||
291 | if (AR_SREV_9287(priv->ah)) | |
292 | priv->ah->led_pin = ATH_LED_PIN_9287; | |
293 | else if (AR_SREV_9271(priv->ah)) | |
294 | priv->ah->led_pin = ATH_LED_PIN_9271; | |
295 | else if (AR_DEVID_7010(priv->ah)) | |
296 | priv->ah->led_pin = ATH_LED_PIN_7010; | |
297 | else | |
298 | priv->ah->led_pin = ATH_LED_PIN_DEF; | |
299 | ||
300 | /* Configure gpio 1 for output */ | |
301 | ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, | |
302 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | |
303 | /* LED off, active low */ | |
304 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); | |
305 | ||
306 | INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work); | |
307 | ||
308 | trigger = ieee80211_get_radio_led_name(priv->hw); | |
309 | snprintf(priv->radio_led.name, sizeof(priv->radio_led.name), | |
310 | "ath9k-%s::radio", wiphy_name(priv->hw->wiphy)); | |
311 | ret = ath9k_register_led(priv, &priv->radio_led, trigger); | |
312 | priv->radio_led.led_type = ATH_LED_RADIO; | |
313 | if (ret) | |
314 | goto fail; | |
315 | ||
316 | trigger = ieee80211_get_assoc_led_name(priv->hw); | |
317 | snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name), | |
318 | "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy)); | |
319 | ret = ath9k_register_led(priv, &priv->assoc_led, trigger); | |
320 | priv->assoc_led.led_type = ATH_LED_ASSOC; | |
321 | if (ret) | |
322 | goto fail; | |
323 | ||
324 | trigger = ieee80211_get_tx_led_name(priv->hw); | |
325 | snprintf(priv->tx_led.name, sizeof(priv->tx_led.name), | |
326 | "ath9k-%s::tx", wiphy_name(priv->hw->wiphy)); | |
327 | ret = ath9k_register_led(priv, &priv->tx_led, trigger); | |
328 | priv->tx_led.led_type = ATH_LED_TX; | |
329 | if (ret) | |
330 | goto fail; | |
331 | ||
332 | trigger = ieee80211_get_rx_led_name(priv->hw); | |
333 | snprintf(priv->rx_led.name, sizeof(priv->rx_led.name), | |
334 | "ath9k-%s::rx", wiphy_name(priv->hw->wiphy)); | |
335 | ret = ath9k_register_led(priv, &priv->rx_led, trigger); | |
336 | priv->rx_led.led_type = ATH_LED_RX; | |
337 | if (ret) | |
338 | goto fail; | |
339 | ||
340 | priv->op_flags &= ~OP_LED_DEINIT; | |
341 | ||
342 | return; | |
343 | ||
344 | fail: | |
345 | cancel_delayed_work_sync(&priv->ath9k_led_blink_work); | |
346 | ath9k_deinit_leds(priv); | |
347 | } | |
348 | ||
349 | /*******************/ | |
350 | /* Rfkill */ | |
351 | /*******************/ | |
352 | ||
353 | static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) | |
354 | { | |
355 | return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == | |
356 | priv->ah->rfkill_polarity; | |
357 | } | |
358 | ||
359 | void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) | |
360 | { | |
361 | struct ath9k_htc_priv *priv = hw->priv; | |
362 | bool blocked = !!ath_is_rfkill_set(priv); | |
363 | ||
364 | wiphy_rfkill_set_hw_state(hw->wiphy, blocked); | |
365 | } | |
366 | ||
367 | void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) | |
368 | { | |
369 | if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | |
370 | wiphy_rfkill_start_polling(priv->hw->wiphy); | |
371 | } | |
372 | ||
373 | void ath9k_htc_radio_enable(struct ieee80211_hw *hw) | |
374 | { | |
375 | struct ath9k_htc_priv *priv = hw->priv; | |
376 | struct ath_hw *ah = priv->ah; | |
377 | struct ath_common *common = ath9k_hw_common(ah); | |
378 | int ret; | |
379 | u8 cmd_rsp; | |
380 | ||
381 | if (!ah->curchan) | |
382 | ah->curchan = ath9k_cmn_get_curchannel(hw, ah); | |
383 | ||
384 | /* Reset the HW */ | |
385 | ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); | |
386 | if (ret) { | |
387 | ath_err(common, | |
388 | "Unable to reset hardware; reset status %d (freq %u MHz)\n", | |
389 | ret, ah->curchan->channel); | |
390 | } | |
391 | ||
b2a5c3df RM |
392 | ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, |
393 | &priv->curtxpow); | |
1e1f4ad2 SM |
394 | |
395 | /* Start RX */ | |
396 | WMI_CMD(WMI_START_RECV_CMDID); | |
397 | ath9k_host_rx_init(priv); | |
398 | ||
399 | /* Start TX */ | |
400 | htc_start(priv->htc); | |
658ef04f | 401 | spin_lock_bh(&priv->tx.tx_lock); |
8e86a547 | 402 | priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP; |
658ef04f | 403 | spin_unlock_bh(&priv->tx.tx_lock); |
1e1f4ad2 SM |
404 | ieee80211_wake_queues(hw); |
405 | ||
406 | WMI_CMD(WMI_ENABLE_INTR_CMDID); | |
407 | ||
408 | /* Enable LED */ | |
409 | ath9k_hw_cfg_output(ah, ah->led_pin, | |
410 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | |
411 | ath9k_hw_set_gpio(ah, ah->led_pin, 0); | |
412 | } | |
413 | ||
414 | void ath9k_htc_radio_disable(struct ieee80211_hw *hw) | |
415 | { | |
416 | struct ath9k_htc_priv *priv = hw->priv; | |
417 | struct ath_hw *ah = priv->ah; | |
418 | struct ath_common *common = ath9k_hw_common(ah); | |
419 | int ret; | |
420 | u8 cmd_rsp; | |
421 | ||
422 | ath9k_htc_ps_wakeup(priv); | |
423 | ||
424 | /* Disable LED */ | |
425 | ath9k_hw_set_gpio(ah, ah->led_pin, 1); | |
426 | ath9k_hw_cfg_gpio_input(ah, ah->led_pin); | |
427 | ||
428 | WMI_CMD(WMI_DISABLE_INTR_CMDID); | |
429 | ||
430 | /* Stop TX */ | |
431 | ieee80211_stop_queues(hw); | |
b587fc81 | 432 | ath9k_htc_tx_drain(priv); |
1e1f4ad2 | 433 | WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); |
1e1f4ad2 SM |
434 | |
435 | /* Stop RX */ | |
436 | WMI_CMD(WMI_STOP_RECV_CMDID); | |
437 | ||
f4c88991 SM |
438 | /* Clear the WMI event queue */ |
439 | ath9k_wmi_event_drain(priv); | |
440 | ||
1e1f4ad2 SM |
441 | /* |
442 | * The MIB counters have to be disabled here, | |
443 | * since the target doesn't do it. | |
444 | */ | |
445 | ath9k_hw_disable_mib_counters(ah); | |
446 | ||
447 | if (!ah->curchan) | |
448 | ah->curchan = ath9k_cmn_get_curchannel(hw, ah); | |
449 | ||
450 | /* Reset the HW */ | |
451 | ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); | |
452 | if (ret) { | |
453 | ath_err(common, | |
454 | "Unable to reset hardware; reset status %d (freq %u MHz)\n", | |
455 | ret, ah->curchan->channel); | |
456 | } | |
457 | ||
458 | /* Disable the PHY */ | |
459 | ath9k_hw_phy_disable(ah); | |
460 | ||
461 | ath9k_htc_ps_restore(priv); | |
462 | ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); | |
463 | } |