Linux Audio

Check our new training course

Loading...
v3.1
  1/*
  2 * Copyright (c) 2008-2011 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 24static 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
 31void 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
 40void ath_init_leds(struct ath_softc *sc)
 41{
 42	int ret;
 43
 44	if (sc->sc_ah->led_pin < 0) {
 45		if (AR_SREV_9287(sc->sc_ah))
 46			sc->sc_ah->led_pin = ATH_LED_PIN_9287;
 47		else if (AR_SREV_9485(sc->sc_ah))
 48			sc->sc_ah->led_pin = ATH_LED_PIN_9485;
 49		else if (AR_SREV_9300(sc->sc_ah))
 50			sc->sc_ah->led_pin = ATH_LED_PIN_9300;
 51		else
 52			sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
 53	}
 54
 55	/* Configure gpio 1 for output */
 56	ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
 57			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
 58	/* LED off, active low */
 59	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
 60
 61	if (!led_blink)
 62		sc->led_cdev.default_trigger =
 63			ieee80211_get_radio_led_name(sc->hw);
 64
 65	snprintf(sc->led_name, sizeof(sc->led_name),
 66		"ath9k-%s", wiphy_name(sc->hw->wiphy));
 67	sc->led_cdev.name = sc->led_name;
 68	sc->led_cdev.brightness_set = ath_led_brightness;
 69
 70	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
 71	if (ret < 0)
 72		return;
 73
 74	sc->led_registered = true;
 75}
 76#endif
 77
 78/*******************/
 79/*	Rfkill	   */
 80/*******************/
 81
 82static bool ath_is_rfkill_set(struct ath_softc *sc)
 83{
 84	struct ath_hw *ah = sc->sc_ah;
 
 85
 86	return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
 
 87				  ah->rfkill_polarity;
 
 
 
 88}
 89
 90void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
 91{
 92	struct ath_softc *sc = hw->priv;
 93	bool blocked = !!ath_is_rfkill_set(sc);
 94
 95	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
 96}
 97
 98void ath_start_rfkill_poll(struct ath_softc *sc)
 99{
100	struct ath_hw *ah = sc->sc_ah;
101
102	if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
103		wiphy_rfkill_start_polling(sc->hw->wiphy);
104}
105
 
 
106/******************/
107/*     BTCOEX     */
108/******************/
109
110/*
111 * Detects if there is any priority bt traffic
112 */
113static void ath_detect_bt_priority(struct ath_softc *sc)
114{
115	struct ath_btcoex *btcoex = &sc->btcoex;
116	struct ath_hw *ah = sc->sc_ah;
117
118	if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
119		btcoex->bt_priority_cnt++;
120
121	if (time_after(jiffies, btcoex->bt_priority_time +
122			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
123		sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
 
124		/* Detect if colocated bt started scanning */
125		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
126			ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
127				"BT scan detected\n");
128			sc->sc_flags |= (SC_OP_BT_SCAN |
129					 SC_OP_BT_PRIORITY_DETECTED);
130		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
131			ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
132				"BT priority traffic detected\n");
133			sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
134		}
135
136		btcoex->bt_priority_cnt = 0;
137		btcoex->bt_priority_time = jiffies;
138	}
139}
140
141static void ath9k_gen_timer_start(struct ath_hw *ah,
142				  struct ath_gen_timer *timer,
143				  u32 trig_timeout,
144				  u32 timer_period)
145{
146	ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period);
147
148	if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
149		ath9k_hw_disable_interrupts(ah);
150		ah->imask |= ATH9K_INT_GENTIMER;
151		ath9k_hw_set_interrupts(ah, ah->imask);
152	}
153}
154
155static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
156{
157	struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
158
159	ath9k_hw_gen_timer_stop(ah, timer);
160
161	/* if no timer is enabled, turn off interrupt mask */
162	if (timer_table->timer_mask.val == 0) {
163		ath9k_hw_disable_interrupts(ah);
164		ah->imask &= ~ATH9K_INT_GENTIMER;
165		ath9k_hw_set_interrupts(ah, ah->imask);
 
 
 
 
166	}
167}
168
169/*
170 * This is the master bt coex timer which runs for every
171 * 45ms, bt traffic will be given priority during 55% of this
172 * period while wlan gets remaining 45%
173 */
174static void ath_btcoex_period_timer(unsigned long data)
175{
176	struct ath_softc *sc = (struct ath_softc *) data;
177	struct ath_hw *ah = sc->sc_ah;
178	struct ath_btcoex *btcoex = &sc->btcoex;
 
179	u32 timer_period;
180	bool is_btscan;
 
 
 
 
 
 
 
 
181
182	ath9k_ps_wakeup(sc);
183	ath_detect_bt_priority(sc);
184
185	is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
 
 
 
186
187	spin_lock_bh(&btcoex->btcoex_lock);
 
 
 
 
188
189	ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL :
190			      btcoex->bt_stomp_type);
 
 
 
 
 
 
 
 
 
 
191
192	spin_unlock_bh(&btcoex->btcoex_lock);
193
194	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
195		if (btcoex->hw_timer_enabled)
196			ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
197
198		timer_period = is_btscan ? btcoex->btscan_no_stomp :
199					   btcoex->btcoex_no_stomp;
200		ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period,
201				      timer_period * 10);
202		btcoex->hw_timer_enabled = true;
203	}
204
205	ath9k_ps_restore(sc);
206	mod_timer(&btcoex->period_timer, jiffies +
207				  msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
 
 
208}
209
210/*
211 * Generic tsf based hw timer which configures weight
212 * registers to time slice between wlan and bt traffic
213 */
214static void ath_btcoex_no_stomp_timer(void *arg)
215{
216	struct ath_softc *sc = (struct ath_softc *)arg;
217	struct ath_hw *ah = sc->sc_ah;
218	struct ath_btcoex *btcoex = &sc->btcoex;
219	struct ath_common *common = ath9k_hw_common(ah);
220	bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
221
222	ath_dbg(common, ATH_DBG_BTCOEX,
223		"no stomp timer running\n");
224
225	ath9k_ps_wakeup(sc);
226	spin_lock_bh(&btcoex->btcoex_lock);
227
228	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
 
 
229		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
230	 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
231		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
232
 
233	spin_unlock_bh(&btcoex->btcoex_lock);
234	ath9k_ps_restore(sc);
235}
236
237int ath_init_btcoex_timer(struct ath_softc *sc)
238{
239	struct ath_btcoex *btcoex = &sc->btcoex;
240
241	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
242	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
243		btcoex->btcoex_period / 100;
244	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
245				   btcoex->btcoex_period / 100;
 
246
247	setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
248			(unsigned long) sc);
 
 
249
250	spin_lock_init(&btcoex->btcoex_lock);
251
252	btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
253			ath_btcoex_no_stomp_timer,
254			ath_btcoex_no_stomp_timer,
255			(void *) sc, AR_FIRST_NDP_TIMER);
256
257	if (!btcoex->no_stomp_timer)
258		return -ENOMEM;
259
260	return 0;
261}
262
263/*
264 * (Re)start btcoex timers
265 */
266void ath9k_btcoex_timer_resume(struct ath_softc *sc)
267{
268	struct ath_btcoex *btcoex = &sc->btcoex;
269	struct ath_hw *ah = sc->sc_ah;
270
271	ath_dbg(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
272		"Starting btcoex timers\n");
 
 
 
273
274	/* make sure duty cycle timer is also stopped when resuming */
275	if (btcoex->hw_timer_enabled)
276		ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
277
278	btcoex->bt_priority_cnt = 0;
279	btcoex->bt_priority_time = jiffies;
280	sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
 
281
282	mod_timer(&btcoex->period_timer, jiffies);
283}
284
285
286/*
287 * Pause btcoex timer and bt duty cycle timer
288 */
289void ath9k_btcoex_timer_pause(struct ath_softc *sc)
290{
291	struct ath_btcoex *btcoex = &sc->btcoex;
292	struct ath_hw *ah = sc->sc_ah;
293
 
 
 
 
 
 
294	del_timer_sync(&btcoex->period_timer);
 
 
 
 
 
 
295
296	if (btcoex->hw_timer_enabled)
297		ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
299	btcoex->hw_timer_enabled = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300}
v4.10.11
  1/*
  2 * Copyright (c) 2008-2011 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
 24
 25static void ath_fill_led_pin(struct ath_softc *sc)
 26{
 27	struct ath_hw *ah = sc->sc_ah;
 28
 29	/* Set default led pin if invalid */
 30	if (ah->led_pin < 0) {
 31		if (AR_SREV_9287(ah))
 32			ah->led_pin = ATH_LED_PIN_9287;
 33		else if (AR_SREV_9485(ah))
 34			ah->led_pin = ATH_LED_PIN_9485;
 35		else if (AR_SREV_9300(ah))
 36			ah->led_pin = ATH_LED_PIN_9300;
 37		else if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
 38			ah->led_pin = ATH_LED_PIN_9462;
 39		else
 40			ah->led_pin = ATH_LED_PIN_DEF;
 41	}
 42
 43	/* Configure gpio for output */
 44	ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
 45				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
 46
 47	/* LED off, active low */
 48	ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
 49}
 50
 51static void ath_led_brightness(struct led_classdev *led_cdev,
 52			       enum led_brightness brightness)
 53{
 54	struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
 55	u32 val = (brightness == LED_OFF);
 56
 57	if (sc->sc_ah->config.led_active_high)
 58		val = !val;
 59
 60	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
 61}
 62
 63void ath_deinit_leds(struct ath_softc *sc)
 64{
 65	if (!sc->led_registered)
 66		return;
 67
 68	ath_led_brightness(&sc->led_cdev, LED_OFF);
 69	led_classdev_unregister(&sc->led_cdev);
 70
 71	ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
 72}
 73
 74void ath_init_leds(struct ath_softc *sc)
 75{
 76	int ret;
 77
 78	if (AR_SREV_9100(sc->sc_ah))
 79		return;
 
 
 
 
 
 
 
 
 80
 81	ath_fill_led_pin(sc);
 
 
 
 
 82
 83	if (!ath9k_led_blink)
 84		sc->led_cdev.default_trigger =
 85			ieee80211_get_radio_led_name(sc->hw);
 86
 87	snprintf(sc->led_name, sizeof(sc->led_name),
 88		"ath9k-%s", wiphy_name(sc->hw->wiphy));
 89	sc->led_cdev.name = sc->led_name;
 90	sc->led_cdev.brightness_set = ath_led_brightness;
 91
 92	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
 93	if (ret < 0)
 94		return;
 95
 96	sc->led_registered = true;
 97}
 98#endif
 99
100/*******************/
101/*	Rfkill	   */
102/*******************/
103
104static bool ath_is_rfkill_set(struct ath_softc *sc)
105{
106	struct ath_hw *ah = sc->sc_ah;
107	bool is_blocked;
108
109	ath9k_ps_wakeup(sc);
110	is_blocked = ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
111				  ah->rfkill_polarity;
112	ath9k_ps_restore(sc);
113
114	return is_blocked;
115}
116
117void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
118{
119	struct ath_softc *sc = hw->priv;
120	bool blocked = !!ath_is_rfkill_set(sc);
121
122	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
123}
124
125void ath_start_rfkill_poll(struct ath_softc *sc)
126{
127	struct ath_hw *ah = sc->sc_ah;
128
129	if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
130		wiphy_rfkill_start_polling(sc->hw->wiphy);
131}
132
133#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
134
135/******************/
136/*     BTCOEX     */
137/******************/
138
139/*
140 * Detects if there is any priority bt traffic
141 */
142static void ath_detect_bt_priority(struct ath_softc *sc)
143{
144	struct ath_btcoex *btcoex = &sc->btcoex;
145	struct ath_hw *ah = sc->sc_ah;
146
147	if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
148		btcoex->bt_priority_cnt++;
149
150	if (time_after(jiffies, btcoex->bt_priority_time +
151			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
152		clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
153		clear_bit(BT_OP_SCAN, &btcoex->op_flags);
154		/* Detect if colocated bt started scanning */
155		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
156			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
157				"BT scan detected\n");
158			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
159			set_bit(BT_OP_SCAN, &btcoex->op_flags);
160		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
161			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
162				"BT priority traffic detected\n");
163			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
164		}
165
166		btcoex->bt_priority_cnt = 0;
167		btcoex->bt_priority_time = jiffies;
168	}
169}
170
171static void ath_mci_ftp_adjust(struct ath_softc *sc)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172{
173	struct ath_btcoex *btcoex = &sc->btcoex;
174	struct ath_mci_profile *mci = &btcoex->mci;
175	struct ath_hw *ah = sc->sc_ah;
176
177	if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
178		if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) &&
179		    (mci->num_pan || mci->num_other_acl))
180			ah->btcoex_hw.mci.stomp_ftp =
181				(sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
182		else
183			ah->btcoex_hw.mci.stomp_ftp = false;
184		btcoex->bt_wait_time = 0;
185		sc->rx.num_pkts = 0;
186	}
187}
188
189/*
190 * This is the master bt coex timer which runs for every
191 * 45ms, bt traffic will be given priority during 55% of this
192 * period while wlan gets remaining 45%
193 */
194static void ath_btcoex_period_timer(unsigned long data)
195{
196	struct ath_softc *sc = (struct ath_softc *) data;
197	struct ath_hw *ah = sc->sc_ah;
198	struct ath_btcoex *btcoex = &sc->btcoex;
199	enum ath_stomp_type stomp_type;
200	u32 timer_period;
201	unsigned long flags;
202
203	spin_lock_irqsave(&sc->sc_pm_lock, flags);
204	if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) {
205		btcoex->bt_wait_time += btcoex->btcoex_period;
206		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
207		goto skip_hw_wakeup;
208	}
209	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
210
211	ath9k_ps_wakeup(sc);
212	spin_lock_bh(&btcoex->btcoex_lock);
213
214	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) {
215		ath9k_mci_update_rssi(sc);
216		ath_mci_ftp_adjust(sc);
217	}
218
219	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
220		ath_detect_bt_priority(sc);
221
222	stomp_type = btcoex->bt_stomp_type;
223	timer_period = btcoex->btcoex_no_stomp;
224
225	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) {
226		if (test_bit(BT_OP_SCAN, &btcoex->op_flags)) {
227			stomp_type = ATH_BTCOEX_STOMP_ALL;
228			timer_period = btcoex->btscan_no_stomp;
229		}
230	} else if (btcoex->stomp_audio >= 5) {
231		stomp_type = ATH_BTCOEX_STOMP_AUDIO;
232		btcoex->stomp_audio = 0;
233	}
234
235	ath9k_hw_btcoex_bt_stomp(ah, stomp_type);
236	ath9k_hw_btcoex_enable(ah);
237
238	spin_unlock_bh(&btcoex->btcoex_lock);
239
240	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp)
241		mod_timer(&btcoex->no_stomp_timer,
242			 jiffies + msecs_to_jiffies(timer_period));
 
 
 
 
 
 
 
243
244	ath9k_ps_restore(sc);
245
246skip_hw_wakeup:
247	mod_timer(&btcoex->period_timer,
248		  jiffies + msecs_to_jiffies(btcoex->btcoex_period));
249}
250
251/*
252 * Generic tsf based hw timer which configures weight
253 * registers to time slice between wlan and bt traffic
254 */
255static void ath_btcoex_no_stomp_timer(unsigned long arg)
256{
257	struct ath_softc *sc = (struct ath_softc *)arg;
258	struct ath_hw *ah = sc->sc_ah;
259	struct ath_btcoex *btcoex = &sc->btcoex;
 
 
 
 
 
260
261	ath9k_ps_wakeup(sc);
262	spin_lock_bh(&btcoex->btcoex_lock);
263
264	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW ||
265	    (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) &&
266	     test_bit(BT_OP_SCAN, &btcoex->op_flags)))
267		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
268	else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
269		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
270
271	ath9k_hw_btcoex_enable(ah);
272	spin_unlock_bh(&btcoex->btcoex_lock);
273	ath9k_ps_restore(sc);
274}
275
276static void ath_init_btcoex_timer(struct ath_softc *sc)
277{
278	struct ath_btcoex *btcoex = &sc->btcoex;
279
280	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
281	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
282		btcoex->btcoex_period / 100;
283	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
284				   btcoex->btcoex_period / 100;
285	btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
286
287	setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
288			(unsigned long) sc);
289	setup_timer(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer,
290			(unsigned long) sc);
291
292	spin_lock_init(&btcoex->btcoex_lock);
 
 
 
 
 
 
 
 
 
 
293}
294
295/*
296 * (Re)start btcoex timers
297 */
298void ath9k_btcoex_timer_resume(struct ath_softc *sc)
299{
300	struct ath_btcoex *btcoex = &sc->btcoex;
301	struct ath_hw *ah = sc->sc_ah;
302
303	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
304	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
305		return;
306
307	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
308
309	/* make sure duty cycle timer is also stopped when resuming */
310	del_timer_sync(&btcoex->no_stomp_timer);
 
311
312	btcoex->bt_priority_cnt = 0;
313	btcoex->bt_priority_time = jiffies;
314	clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
315	clear_bit(BT_OP_SCAN, &btcoex->op_flags);
316
317	mod_timer(&btcoex->period_timer, jiffies);
318}
319
 
320/*
321 * Pause btcoex timer and bt duty cycle timer
322 */
323void ath9k_btcoex_timer_pause(struct ath_softc *sc)
324{
325	struct ath_btcoex *btcoex = &sc->btcoex;
326	struct ath_hw *ah = sc->sc_ah;
327
328	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
329	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
330		return;
331
332	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n");
333
334	del_timer_sync(&btcoex->period_timer);
335	del_timer_sync(&btcoex->no_stomp_timer);
336}
337
338void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
339{
340	struct ath_btcoex *btcoex = &sc->btcoex;
341
342	del_timer_sync(&btcoex->no_stomp_timer);
343}
344
345u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
346{
347	struct ath_btcoex *btcoex = &sc->btcoex;
348	struct ath_mci_profile *mci = &sc->btcoex.mci;
349	u16 aggr_limit = 0;
350
351	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit)
352		aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4;
353	else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags))
354		aggr_limit = min((max_4ms_framelen * 3) / 8,
355				 (u32)ATH_AMPDU_LIMIT_MAX);
356
357	return aggr_limit;
358}
359
360void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status)
361{
362	if (status & ATH9K_INT_MCI)
363		ath_mci_intr(sc);
364}
365
366void ath9k_start_btcoex(struct ath_softc *sc)
367{
368	struct ath_hw *ah = sc->sc_ah;
369
370	if (ah->btcoex_hw.enabled ||
371	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
372		return;
373
374	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
375		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
376					   AR_STOMP_LOW_WLAN_WGHT, 0);
377	else
378		ath9k_hw_btcoex_set_weight(ah, 0, 0,
379					   ATH_BTCOEX_STOMP_NONE);
380	ath9k_hw_btcoex_enable(ah);
381	ath9k_btcoex_timer_resume(sc);
382}
383
384void ath9k_stop_btcoex(struct ath_softc *sc)
385{
386	struct ath_hw *ah = sc->sc_ah;
387
388	if (!ah->btcoex_hw.enabled ||
389	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
390		return;
391
392	ath9k_btcoex_timer_pause(sc);
393	ath9k_hw_btcoex_disable(ah);
394
395	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
396		ath_mci_flush_profile(&sc->btcoex.mci);
397}
398
399void ath9k_deinit_btcoex(struct ath_softc *sc)
400{
401	struct ath_hw *ah = sc->sc_ah;
402
403	if (ath9k_hw_mci_is_enabled(ah))
404		ath_mci_cleanup(sc);
405	else {
406		enum ath_btcoex_scheme scheme = ath9k_hw_get_btcoex_scheme(ah);
407
408		if (scheme == ATH_BTCOEX_CFG_2WIRE ||
409		    scheme == ATH_BTCOEX_CFG_3WIRE)
410			ath9k_hw_btcoex_deinit(sc->sc_ah);
411	}
412}
413
414int ath9k_init_btcoex(struct ath_softc *sc)
415{
416	struct ath_txq *txq;
417	struct ath_hw *ah = sc->sc_ah;
418	int r;
419
420	ath9k_hw_btcoex_init_scheme(ah);
421
422	switch (ath9k_hw_get_btcoex_scheme(sc->sc_ah)) {
423	case ATH_BTCOEX_CFG_NONE:
424		break;
425	case ATH_BTCOEX_CFG_2WIRE:
426		ath9k_hw_btcoex_init_2wire(sc->sc_ah);
427		break;
428	case ATH_BTCOEX_CFG_3WIRE:
429		ath9k_hw_btcoex_init_3wire(sc->sc_ah);
430		ath_init_btcoex_timer(sc);
431		txq = sc->tx.txq_map[IEEE80211_AC_BE];
432		ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum);
433		break;
434	case ATH_BTCOEX_CFG_MCI:
435		ath_init_btcoex_timer(sc);
436
437		sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
438		INIT_LIST_HEAD(&sc->btcoex.mci.info);
439		ath9k_hw_btcoex_init_mci(ah);
440
441		r = ath_mci_setup(sc);
442		if (r)
443			return r;
444
445		break;
446	default:
447		WARN_ON(1);
448		break;
449	}
450
451	return 0;
452}
453
454static int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
455{
456	struct ath_btcoex *btcoex = &sc->btcoex;
457	struct ath_mci_profile *mci = &btcoex->mci;
458	struct ath_hw *ah = sc->sc_ah;
459	struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
460	u32 len = 0;
461	int i;
462
463	ATH_DUMP_BTCOEX("Total BT profiles", NUM_PROF(mci));
464	ATH_DUMP_BTCOEX("MGMT", mci->num_mgmt);
465	ATH_DUMP_BTCOEX("SCO", mci->num_sco);
466	ATH_DUMP_BTCOEX("A2DP", mci->num_a2dp);
467	ATH_DUMP_BTCOEX("HID", mci->num_hid);
468	ATH_DUMP_BTCOEX("PAN", mci->num_pan);
469	ATH_DUMP_BTCOEX("ACL", mci->num_other_acl);
470	ATH_DUMP_BTCOEX("BDR", mci->num_bdr);
471	ATH_DUMP_BTCOEX("Aggr. Limit", mci->aggr_limit);
472	ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type);
473	ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period);
474	ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle);
475	ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time);
476	ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx);
477	ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count);
478
479	len += scnprintf(buf + len, size - len, "BT Weights: ");
480	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
481		len += scnprintf(buf + len, size - len, "%08x ",
482				 btcoex_hw->bt_weight[i]);
483	len += scnprintf(buf + len, size - len, "\n");
484	len += scnprintf(buf + len, size - len, "WLAN Weights: ");
485	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
486		len += scnprintf(buf + len, size - len, "%08x ",
487				 btcoex_hw->wlan_weight[i]);
488	len += scnprintf(buf + len, size - len, "\n");
489	len += scnprintf(buf + len, size - len, "Tx Priorities: ");
490	for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++)
491		len += scnprintf(buf + len, size - len, "%08x ",
492				btcoex_hw->tx_prio[i]);
493
494	len += scnprintf(buf + len, size - len, "\n");
495
496	return len;
497}
498
499static int ath9k_dump_legacy_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
500{
501
502	struct ath_btcoex *btcoex = &sc->btcoex;
503	u32 len = 0;
504
505	ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type);
506	ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period);
507	ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle);
508	ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time);
509
510	return len;
511}
512
513int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
514{
515	if (ath9k_hw_mci_is_enabled(sc->sc_ah))
516		return ath9k_dump_mci_btcoex(sc, buf, size);
517	else
518		return ath9k_dump_legacy_btcoex(sc, buf, size);
519}
520
521#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */