blob: e369bf7e575fbdb7fb2670319e9c7ff80f2c79d4 [file] [log] [blame]
Sujith0fca65c2010-01-08 10:36:00 +05301/*
2 * Copyright (c) 2008-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/********************************/
20/* LED functions */
21/********************************/
22
Felix Fietkau0cf55c22011-02-27 22:26:40 +010023#ifdef CONFIG_MAC80211_LEDS
Sujith0fca65c2010-01-08 10:36:00 +053024static void ath_led_brightness(struct led_classdev *led_cdev,
25 enum led_brightness brightness)
26{
Felix Fietkau0cf55c22011-02-27 22:26:40 +010027 struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
28 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF));
Sujith0fca65c2010-01-08 10:36:00 +053029}
30
31void ath_deinit_leds(struct ath_softc *sc)
32{
Felix Fietkau0cf55c22011-02-27 22:26:40 +010033 if (!sc->led_registered)
34 return;
35
36 ath_led_brightness(&sc->led_cdev, LED_OFF);
37 led_classdev_unregister(&sc->led_cdev);
Sujith0fca65c2010-01-08 10:36:00 +053038}
39
40void ath_init_leds(struct ath_softc *sc)
41{
Sujith0fca65c2010-01-08 10:36:00 +053042 int ret;
43
44 if (AR_SREV_9287(sc->sc_ah))
45 sc->sc_ah->led_pin = ATH_LED_PIN_9287;
46 else
47 sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
48
49 /* Configure gpio 1 for output */
50 ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
51 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
52 /* LED off, active low */
53 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
54
Felix Fietkau0cf55c22011-02-27 22:26:40 +010055 if (!led_blink)
56 sc->led_cdev.default_trigger =
57 ieee80211_get_radio_led_name(sc->hw);
Sujith0fca65c2010-01-08 10:36:00 +053058
Felix Fietkau0cf55c22011-02-27 22:26:40 +010059 snprintf(sc->led_name, sizeof(sc->led_name),
60 "ath9k-%s", wiphy_name(sc->hw->wiphy));
61 sc->led_cdev.name = sc->led_name;
62 sc->led_cdev.brightness_set = ath_led_brightness;
Sujith0fca65c2010-01-08 10:36:00 +053063
Felix Fietkau0cf55c22011-02-27 22:26:40 +010064 ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
65 if (ret < 0)
66 return;
Sujith0fca65c2010-01-08 10:36:00 +053067
Felix Fietkau0cf55c22011-02-27 22:26:40 +010068 sc->led_registered = true;
Sujith0fca65c2010-01-08 10:36:00 +053069}
Felix Fietkau0cf55c22011-02-27 22:26:40 +010070#endif
Sujith0fca65c2010-01-08 10:36:00 +053071
72/*******************/
73/* Rfkill */
74/*******************/
75
76static bool ath_is_rfkill_set(struct ath_softc *sc)
77{
78 struct ath_hw *ah = sc->sc_ah;
79
80 return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
81 ah->rfkill_polarity;
82}
83
84void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
85{
Felix Fietkau9ac58612011-01-24 19:23:18 +010086 struct ath_softc *sc = hw->priv;
Sujith0fca65c2010-01-08 10:36:00 +053087 bool blocked = !!ath_is_rfkill_set(sc);
88
89 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
90}
91
92void ath_start_rfkill_poll(struct ath_softc *sc)
93{
94 struct ath_hw *ah = sc->sc_ah;
95
96 if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
97 wiphy_rfkill_start_polling(sc->hw->wiphy);
98}
99
100/******************/
101/* BTCOEX */
102/******************/
103
104/*
105 * Detects if there is any priority bt traffic
106 */
107static void ath_detect_bt_priority(struct ath_softc *sc)
108{
109 struct ath_btcoex *btcoex = &sc->btcoex;
110 struct ath_hw *ah = sc->sc_ah;
111
112 if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
113 btcoex->bt_priority_cnt++;
114
115 if (time_after(jiffies, btcoex->bt_priority_time +
116 msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530117 sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
118 /* Detect if colocated bt started scanning */
119 if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
Joe Perches226afe62010-12-02 19:12:37 -0800120 ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
121 "BT scan detected\n");
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530122 sc->sc_flags |= (SC_OP_BT_SCAN |
123 SC_OP_BT_PRIORITY_DETECTED);
124 } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
Joe Perches226afe62010-12-02 19:12:37 -0800125 ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
126 "BT priority traffic detected\n");
Sujith0fca65c2010-01-08 10:36:00 +0530127 sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
Sujith0fca65c2010-01-08 10:36:00 +0530128 }
129
130 btcoex->bt_priority_cnt = 0;
131 btcoex->bt_priority_time = jiffies;
132 }
133}
134
Sujith0fca65c2010-01-08 10:36:00 +0530135static void ath9k_gen_timer_start(struct ath_hw *ah,
136 struct ath_gen_timer *timer,
137 u32 timer_next,
138 u32 timer_period)
139{
Sujith0fca65c2010-01-08 10:36:00 +0530140 ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
141
Pavel Roskin30691682010-03-31 18:05:31 -0400142 if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
Felix Fietkau4df30712010-11-08 20:54:47 +0100143 ath9k_hw_disable_interrupts(ah);
Pavel Roskin30691682010-03-31 18:05:31 -0400144 ah->imask |= ATH9K_INT_GENTIMER;
145 ath9k_hw_set_interrupts(ah, ah->imask);
Sujith0fca65c2010-01-08 10:36:00 +0530146 }
147}
148
149static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
150{
Sujith0fca65c2010-01-08 10:36:00 +0530151 struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
152
153 ath9k_hw_gen_timer_stop(ah, timer);
154
155 /* if no timer is enabled, turn off interrupt mask */
156 if (timer_table->timer_mask.val == 0) {
Felix Fietkau4df30712010-11-08 20:54:47 +0100157 ath9k_hw_disable_interrupts(ah);
Pavel Roskin30691682010-03-31 18:05:31 -0400158 ah->imask &= ~ATH9K_INT_GENTIMER;
159 ath9k_hw_set_interrupts(ah, ah->imask);
Sujith0fca65c2010-01-08 10:36:00 +0530160 }
161}
162
163/*
164 * This is the master bt coex timer which runs for every
165 * 45ms, bt traffic will be given priority during 55% of this
166 * period while wlan gets remaining 45%
167 */
168static void ath_btcoex_period_timer(unsigned long data)
169{
170 struct ath_softc *sc = (struct ath_softc *) data;
171 struct ath_hw *ah = sc->sc_ah;
172 struct ath_btcoex *btcoex = &sc->btcoex;
Vivek Natarajand99eeb82010-08-18 19:57:48 +0530173 struct ath_common *common = ath9k_hw_common(ah);
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530174 u32 timer_period;
175 bool is_btscan;
Sujith0fca65c2010-01-08 10:36:00 +0530176
177 ath_detect_bt_priority(sc);
178
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530179 is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
180
Sujith0fca65c2010-01-08 10:36:00 +0530181 spin_lock_bh(&btcoex->btcoex_lock);
182
Vivek Natarajand99eeb82010-08-18 19:57:48 +0530183 ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL :
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530184 btcoex->bt_stomp_type);
Sujith0fca65c2010-01-08 10:36:00 +0530185
186 spin_unlock_bh(&btcoex->btcoex_lock);
187
188 if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
189 if (btcoex->hw_timer_enabled)
190 ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
191
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530192 timer_period = is_btscan ? btcoex->btscan_no_stomp :
193 btcoex->btcoex_no_stomp;
Felix Fietkau8eb1dab2010-10-15 20:03:32 +0200194 ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, 0,
195 timer_period * 10);
Sujith0fca65c2010-01-08 10:36:00 +0530196 btcoex->hw_timer_enabled = true;
197 }
198
199 mod_timer(&btcoex->period_timer, jiffies +
200 msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
201}
202
203/*
204 * Generic tsf based hw timer which configures weight
205 * registers to time slice between wlan and bt traffic
206 */
207static void ath_btcoex_no_stomp_timer(void *arg)
208{
209 struct ath_softc *sc = (struct ath_softc *)arg;
210 struct ath_hw *ah = sc->sc_ah;
211 struct ath_btcoex *btcoex = &sc->btcoex;
Vivek Natarajand99eeb82010-08-18 19:57:48 +0530212 struct ath_common *common = ath9k_hw_common(ah);
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530213 bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
Sujith0fca65c2010-01-08 10:36:00 +0530214
Joe Perches226afe62010-12-02 19:12:37 -0800215 ath_dbg(common, ATH_DBG_BTCOEX,
216 "no stomp timer running\n");
Sujith0fca65c2010-01-08 10:36:00 +0530217
218 spin_lock_bh(&btcoex->btcoex_lock);
219
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530220 if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
Vivek Natarajand99eeb82010-08-18 19:57:48 +0530221 ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE);
Sujith0fca65c2010-01-08 10:36:00 +0530222 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
Vivek Natarajand99eeb82010-08-18 19:57:48 +0530223 ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW);
Sujith0fca65c2010-01-08 10:36:00 +0530224
225 spin_unlock_bh(&btcoex->btcoex_lock);
226}
227
228int ath_init_btcoex_timer(struct ath_softc *sc)
229{
230 struct ath_btcoex *btcoex = &sc->btcoex;
231
232 btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
233 btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
234 btcoex->btcoex_period / 100;
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530235 btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
236 btcoex->btcoex_period / 100;
Sujith0fca65c2010-01-08 10:36:00 +0530237
238 setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
239 (unsigned long) sc);
240
241 spin_lock_init(&btcoex->btcoex_lock);
242
243 btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
244 ath_btcoex_no_stomp_timer,
245 ath_btcoex_no_stomp_timer,
246 (void *) sc, AR_FIRST_NDP_TIMER);
247
248 if (!btcoex->no_stomp_timer)
249 return -ENOMEM;
250
251 return 0;
252}
253
254/*
255 * (Re)start btcoex timers
256 */
257void ath9k_btcoex_timer_resume(struct ath_softc *sc)
258{
259 struct ath_btcoex *btcoex = &sc->btcoex;
260 struct ath_hw *ah = sc->sc_ah;
261
Joe Perches226afe62010-12-02 19:12:37 -0800262 ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
263 "Starting btcoex timers\n");
Sujith0fca65c2010-01-08 10:36:00 +0530264
265 /* make sure duty cycle timer is also stopped when resuming */
266 if (btcoex->hw_timer_enabled)
267 ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
268
269 btcoex->bt_priority_cnt = 0;
270 btcoex->bt_priority_time = jiffies;
Vasanthakumar Thiagarajan58da1312010-01-21 11:17:27 +0530271 sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
Sujith0fca65c2010-01-08 10:36:00 +0530272
273 mod_timer(&btcoex->period_timer, jiffies);
274}
275
276
277/*
278 * Pause btcoex timer and bt duty cycle timer
279 */
280void ath9k_btcoex_timer_pause(struct ath_softc *sc)
281{
282 struct ath_btcoex *btcoex = &sc->btcoex;
283 struct ath_hw *ah = sc->sc_ah;
284
285 del_timer_sync(&btcoex->period_timer);
286
287 if (btcoex->hw_timer_enabled)
288 ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
289
290 btcoex->hw_timer_enabled = false;
291}