1 /*
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 
23 #ifdef CONFIG_MAC80211_LEDS
ath_led_brightness(struct led_classdev * led_cdev,enum led_brightness brightness)24 static void ath_led_brightness(struct led_classdev *led_cdev,
25 			       enum led_brightness brightness)
26 {
27 	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));
29 }
30 
ath_deinit_leds(struct ath_softc * sc)31 void ath_deinit_leds(struct ath_softc *sc)
32 {
33 	if (!sc->led_registered)
34 		return;
35 
36 	ath_led_brightness(&sc->led_cdev, LED_OFF);
37 	led_classdev_unregister(&sc->led_cdev);
38 }
39 
ath_init_leds(struct ath_softc * sc)40 void ath_init_leds(struct ath_softc *sc)
41 {
42 	int ret;
43 
44 	if (AR_SREV_9287(sc->sc_ah))
45 		sc->sc_ah->led_pin = ATH_LED_PIN_9287;
46 	else if (AR_SREV_9485(sc->sc_ah))
47 		sc->sc_ah->led_pin = ATH_LED_PIN_9485;
48 	else
49 		sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
50 
51 	/* Configure gpio 1 for output */
52 	ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
53 			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
54 	/* LED off, active low */
55 	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
56 
57 	if (!led_blink)
58 		sc->led_cdev.default_trigger =
59 			ieee80211_get_radio_led_name(sc->hw);
60 
61 	snprintf(sc->led_name, sizeof(sc->led_name),
62 		"ath9k-%s", wiphy_name(sc->hw->wiphy));
63 	sc->led_cdev.name = sc->led_name;
64 	sc->led_cdev.brightness_set = ath_led_brightness;
65 
66 	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
67 	if (ret < 0)
68 		return;
69 
70 	sc->led_registered = true;
71 }
72 #endif
73 
74 /*******************/
75 /*	Rfkill	   */
76 /*******************/
77 
ath_is_rfkill_set(struct ath_softc * sc)78 static bool ath_is_rfkill_set(struct ath_softc *sc)
79 {
80 	struct ath_hw *ah = sc->sc_ah;
81 
82 	return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
83 				  ah->rfkill_polarity;
84 }
85 
ath9k_rfkill_poll_state(struct ieee80211_hw * hw)86 void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
87 {
88 	struct ath_softc *sc = hw->priv;
89 	bool blocked = !!ath_is_rfkill_set(sc);
90 
91 	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
92 }
93 
ath_start_rfkill_poll(struct ath_softc * sc)94 void ath_start_rfkill_poll(struct ath_softc *sc)
95 {
96 	struct ath_hw *ah = sc->sc_ah;
97 
98 	if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
99 		wiphy_rfkill_start_polling(sc->hw->wiphy);
100 }
101 
102 /******************/
103 /*     BTCOEX     */
104 /******************/
105 
106 /*
107  * Detects if there is any priority bt traffic
108  */
ath_detect_bt_priority(struct ath_softc * sc)109 static void ath_detect_bt_priority(struct ath_softc *sc)
110 {
111 	struct ath_btcoex *btcoex = &sc->btcoex;
112 	struct ath_hw *ah = sc->sc_ah;
113 
114 	if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
115 		btcoex->bt_priority_cnt++;
116 
117 	if (time_after(jiffies, btcoex->bt_priority_time +
118 			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
119 		sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
120 		/* Detect if colocated bt started scanning */
121 		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
122 			ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
123 				"BT scan detected\n");
124 			sc->sc_flags |= (SC_OP_BT_SCAN |
125 					 SC_OP_BT_PRIORITY_DETECTED);
126 		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
127 			ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
128 				"BT priority traffic detected\n");
129 			sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
130 		}
131 
132 		btcoex->bt_priority_cnt = 0;
133 		btcoex->bt_priority_time = jiffies;
134 	}
135 }
136 
ath9k_gen_timer_start(struct ath_hw * ah,struct ath_gen_timer * timer,u32 timer_next,u32 timer_period)137 static void ath9k_gen_timer_start(struct ath_hw *ah,
138 				  struct ath_gen_timer *timer,
139 				  u32 timer_next,
140 				  u32 timer_period)
141 {
142 	ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
143 
144 	if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
145 		ath9k_hw_disable_interrupts(ah);
146 		ah->imask |= ATH9K_INT_GENTIMER;
147 		ath9k_hw_set_interrupts(ah, ah->imask);
148 	}
149 }
150 
ath9k_gen_timer_stop(struct ath_hw * ah,struct ath_gen_timer * timer)151 static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
152 {
153 	struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
154 
155 	ath9k_hw_gen_timer_stop(ah, timer);
156 
157 	/* if no timer is enabled, turn off interrupt mask */
158 	if (timer_table->timer_mask.val == 0) {
159 		ath9k_hw_disable_interrupts(ah);
160 		ah->imask &= ~ATH9K_INT_GENTIMER;
161 		ath9k_hw_set_interrupts(ah, ah->imask);
162 	}
163 }
164 
165 /*
166  * This is the master bt coex timer which runs for every
167  * 45ms, bt traffic will be given priority during 55% of this
168  * period while wlan gets remaining 45%
169  */
ath_btcoex_period_timer(unsigned long data)170 static void ath_btcoex_period_timer(unsigned long data)
171 {
172 	struct ath_softc *sc = (struct ath_softc *) data;
173 	struct ath_hw *ah = sc->sc_ah;
174 	struct ath_btcoex *btcoex = &sc->btcoex;
175 	struct ath_common *common = ath9k_hw_common(ah);
176 	u32 timer_period;
177 	bool is_btscan;
178 
179 	ath_detect_bt_priority(sc);
180 
181 	is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
182 
183 	spin_lock_bh(&btcoex->btcoex_lock);
184 
185 	ath9k_cmn_btcoex_bt_stomp(common, is_btscan ? ATH_BTCOEX_STOMP_ALL :
186 			      btcoex->bt_stomp_type);
187 
188 	spin_unlock_bh(&btcoex->btcoex_lock);
189 
190 	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
191 		if (btcoex->hw_timer_enabled)
192 			ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
193 
194 		timer_period = is_btscan ? btcoex->btscan_no_stomp :
195 					   btcoex->btcoex_no_stomp;
196 		ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, 0,
197 				      timer_period * 10);
198 		btcoex->hw_timer_enabled = true;
199 	}
200 
201 	mod_timer(&btcoex->period_timer, jiffies +
202 				  msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
203 }
204 
205 /*
206  * Generic tsf based hw timer which configures weight
207  * registers to time slice between wlan and bt traffic
208  */
ath_btcoex_no_stomp_timer(void * arg)209 static void ath_btcoex_no_stomp_timer(void *arg)
210 {
211 	struct ath_softc *sc = (struct ath_softc *)arg;
212 	struct ath_hw *ah = sc->sc_ah;
213 	struct ath_btcoex *btcoex = &sc->btcoex;
214 	struct ath_common *common = ath9k_hw_common(ah);
215 	bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
216 
217 	ath_dbg(common, ATH_DBG_BTCOEX,
218 		"no stomp timer running\n");
219 
220 	spin_lock_bh(&btcoex->btcoex_lock);
221 
222 	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
223 		ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_NONE);
224 	 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
225 		ath9k_cmn_btcoex_bt_stomp(common, ATH_BTCOEX_STOMP_LOW);
226 
227 	spin_unlock_bh(&btcoex->btcoex_lock);
228 }
229 
ath_init_btcoex_timer(struct ath_softc * sc)230 int ath_init_btcoex_timer(struct ath_softc *sc)
231 {
232 	struct ath_btcoex *btcoex = &sc->btcoex;
233 
234 	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
235 	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
236 		btcoex->btcoex_period / 100;
237 	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
238 				   btcoex->btcoex_period / 100;
239 
240 	setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
241 			(unsigned long) sc);
242 
243 	spin_lock_init(&btcoex->btcoex_lock);
244 
245 	btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
246 			ath_btcoex_no_stomp_timer,
247 			ath_btcoex_no_stomp_timer,
248 			(void *) sc, AR_FIRST_NDP_TIMER);
249 
250 	if (!btcoex->no_stomp_timer)
251 		return -ENOMEM;
252 
253 	return 0;
254 }
255 
256 /*
257  * (Re)start btcoex timers
258  */
ath9k_btcoex_timer_resume(struct ath_softc * sc)259 void ath9k_btcoex_timer_resume(struct ath_softc *sc)
260 {
261 	struct ath_btcoex *btcoex = &sc->btcoex;
262 	struct ath_hw *ah = sc->sc_ah;
263 
264 	ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
265 		"Starting btcoex timers\n");
266 
267 	/* make sure duty cycle timer is also stopped when resuming */
268 	if (btcoex->hw_timer_enabled)
269 		ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
270 
271 	btcoex->bt_priority_cnt = 0;
272 	btcoex->bt_priority_time = jiffies;
273 	sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
274 
275 	mod_timer(&btcoex->period_timer, jiffies);
276 }
277 
278 
279 /*
280  * Pause btcoex timer and bt duty cycle timer
281  */
ath9k_btcoex_timer_pause(struct ath_softc * sc)282 void ath9k_btcoex_timer_pause(struct ath_softc *sc)
283 {
284 	struct ath_btcoex *btcoex = &sc->btcoex;
285 	struct ath_hw *ah = sc->sc_ah;
286 
287 	del_timer_sync(&btcoex->period_timer);
288 
289 	if (btcoex->hw_timer_enabled)
290 		ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
291 
292 	btcoex->hw_timer_enabled = false;
293 }
294