xref: /linux/drivers/net/wireless/ath/ath9k/gpio.c (revision 58da1318ee92ad3fe7917278d596768bbe441850)
10fca65c1SSujith /*
20fca65c1SSujith  * Copyright (c) 2008-2009 Atheros Communications Inc.
30fca65c1SSujith  *
40fca65c1SSujith  * Permission to use, copy, modify, and/or distribute this software for any
50fca65c1SSujith  * purpose with or without fee is hereby granted, provided that the above
60fca65c1SSujith  * copyright notice and this permission notice appear in all copies.
70fca65c1SSujith  *
80fca65c1SSujith  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
90fca65c1SSujith  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
100fca65c1SSujith  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
110fca65c1SSujith  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
120fca65c1SSujith  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
130fca65c1SSujith  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
140fca65c1SSujith  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
150fca65c1SSujith  */
160fca65c1SSujith 
170fca65c1SSujith #include "ath9k.h"
180fca65c1SSujith 
190fca65c1SSujith /********************************/
200fca65c1SSujith /*	 LED functions		*/
210fca65c1SSujith /********************************/
220fca65c1SSujith 
230fca65c1SSujith static void ath_led_blink_work(struct work_struct *work)
240fca65c1SSujith {
250fca65c1SSujith 	struct ath_softc *sc = container_of(work, struct ath_softc,
260fca65c1SSujith 					    ath_led_blink_work.work);
270fca65c1SSujith 
280fca65c1SSujith 	if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED))
290fca65c1SSujith 		return;
300fca65c1SSujith 
310fca65c1SSujith 	if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
320fca65c1SSujith 	    (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
330fca65c1SSujith 		ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
340fca65c1SSujith 	else
350fca65c1SSujith 		ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
360fca65c1SSujith 				  (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
370fca65c1SSujith 
380fca65c1SSujith 	ieee80211_queue_delayed_work(sc->hw,
390fca65c1SSujith 				     &sc->ath_led_blink_work,
400fca65c1SSujith 				     (sc->sc_flags & SC_OP_LED_ON) ?
410fca65c1SSujith 					msecs_to_jiffies(sc->led_off_duration) :
420fca65c1SSujith 					msecs_to_jiffies(sc->led_on_duration));
430fca65c1SSujith 
440fca65c1SSujith 	sc->led_on_duration = sc->led_on_cnt ?
450fca65c1SSujith 			max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) :
460fca65c1SSujith 			ATH_LED_ON_DURATION_IDLE;
470fca65c1SSujith 	sc->led_off_duration = sc->led_off_cnt ?
480fca65c1SSujith 			max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) :
490fca65c1SSujith 			ATH_LED_OFF_DURATION_IDLE;
500fca65c1SSujith 	sc->led_on_cnt = sc->led_off_cnt = 0;
510fca65c1SSujith 	if (sc->sc_flags & SC_OP_LED_ON)
520fca65c1SSujith 		sc->sc_flags &= ~SC_OP_LED_ON;
530fca65c1SSujith 	else
540fca65c1SSujith 		sc->sc_flags |= SC_OP_LED_ON;
550fca65c1SSujith }
560fca65c1SSujith 
570fca65c1SSujith static void ath_led_brightness(struct led_classdev *led_cdev,
580fca65c1SSujith 			       enum led_brightness brightness)
590fca65c1SSujith {
600fca65c1SSujith 	struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
610fca65c1SSujith 	struct ath_softc *sc = led->sc;
620fca65c1SSujith 
630fca65c1SSujith 	switch (brightness) {
640fca65c1SSujith 	case LED_OFF:
650fca65c1SSujith 		if (led->led_type == ATH_LED_ASSOC ||
660fca65c1SSujith 		    led->led_type == ATH_LED_RADIO) {
670fca65c1SSujith 			ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
680fca65c1SSujith 				(led->led_type == ATH_LED_RADIO));
690fca65c1SSujith 			sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
700fca65c1SSujith 			if (led->led_type == ATH_LED_RADIO)
710fca65c1SSujith 				sc->sc_flags &= ~SC_OP_LED_ON;
720fca65c1SSujith 		} else {
730fca65c1SSujith 			sc->led_off_cnt++;
740fca65c1SSujith 		}
750fca65c1SSujith 		break;
760fca65c1SSujith 	case LED_FULL:
770fca65c1SSujith 		if (led->led_type == ATH_LED_ASSOC) {
780fca65c1SSujith 			sc->sc_flags |= SC_OP_LED_ASSOCIATED;
790fca65c1SSujith 			ieee80211_queue_delayed_work(sc->hw,
800fca65c1SSujith 						     &sc->ath_led_blink_work, 0);
810fca65c1SSujith 		} else if (led->led_type == ATH_LED_RADIO) {
820fca65c1SSujith 			ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
830fca65c1SSujith 			sc->sc_flags |= SC_OP_LED_ON;
840fca65c1SSujith 		} else {
850fca65c1SSujith 			sc->led_on_cnt++;
860fca65c1SSujith 		}
870fca65c1SSujith 		break;
880fca65c1SSujith 	default:
890fca65c1SSujith 		break;
900fca65c1SSujith 	}
910fca65c1SSujith }
920fca65c1SSujith 
930fca65c1SSujith static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
940fca65c1SSujith 			    char *trigger)
950fca65c1SSujith {
960fca65c1SSujith 	int ret;
970fca65c1SSujith 
980fca65c1SSujith 	led->sc = sc;
990fca65c1SSujith 	led->led_cdev.name = led->name;
1000fca65c1SSujith 	led->led_cdev.default_trigger = trigger;
1010fca65c1SSujith 	led->led_cdev.brightness_set = ath_led_brightness;
1020fca65c1SSujith 
1030fca65c1SSujith 	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
1040fca65c1SSujith 	if (ret)
1050fca65c1SSujith 		ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1060fca65c1SSujith 			  "Failed to register led:%s", led->name);
1070fca65c1SSujith 	else
1080fca65c1SSujith 		led->registered = 1;
1090fca65c1SSujith 	return ret;
1100fca65c1SSujith }
1110fca65c1SSujith 
1120fca65c1SSujith static void ath_unregister_led(struct ath_led *led)
1130fca65c1SSujith {
1140fca65c1SSujith 	if (led->registered) {
1150fca65c1SSujith 		led_classdev_unregister(&led->led_cdev);
1160fca65c1SSujith 		led->registered = 0;
1170fca65c1SSujith 	}
1180fca65c1SSujith }
1190fca65c1SSujith 
1200fca65c1SSujith void ath_deinit_leds(struct ath_softc *sc)
1210fca65c1SSujith {
1220fca65c1SSujith 	ath_unregister_led(&sc->assoc_led);
1230fca65c1SSujith 	sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
1240fca65c1SSujith 	ath_unregister_led(&sc->tx_led);
1250fca65c1SSujith 	ath_unregister_led(&sc->rx_led);
1260fca65c1SSujith 	ath_unregister_led(&sc->radio_led);
1270fca65c1SSujith 	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
1280fca65c1SSujith }
1290fca65c1SSujith 
1300fca65c1SSujith void ath_init_leds(struct ath_softc *sc)
1310fca65c1SSujith {
1320fca65c1SSujith 	char *trigger;
1330fca65c1SSujith 	int ret;
1340fca65c1SSujith 
1350fca65c1SSujith 	if (AR_SREV_9287(sc->sc_ah))
1360fca65c1SSujith 		sc->sc_ah->led_pin = ATH_LED_PIN_9287;
1370fca65c1SSujith 	else
1380fca65c1SSujith 		sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
1390fca65c1SSujith 
1400fca65c1SSujith 	/* Configure gpio 1 for output */
1410fca65c1SSujith 	ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
1420fca65c1SSujith 			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1430fca65c1SSujith 	/* LED off, active low */
1440fca65c1SSujith 	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
1450fca65c1SSujith 
1460fca65c1SSujith 	INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work);
1470fca65c1SSujith 
1480fca65c1SSujith 	trigger = ieee80211_get_radio_led_name(sc->hw);
1490fca65c1SSujith 	snprintf(sc->radio_led.name, sizeof(sc->radio_led.name),
1500fca65c1SSujith 		"ath9k-%s::radio", wiphy_name(sc->hw->wiphy));
1510fca65c1SSujith 	ret = ath_register_led(sc, &sc->radio_led, trigger);
1520fca65c1SSujith 	sc->radio_led.led_type = ATH_LED_RADIO;
1530fca65c1SSujith 	if (ret)
1540fca65c1SSujith 		goto fail;
1550fca65c1SSujith 
1560fca65c1SSujith 	trigger = ieee80211_get_assoc_led_name(sc->hw);
1570fca65c1SSujith 	snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name),
1580fca65c1SSujith 		"ath9k-%s::assoc", wiphy_name(sc->hw->wiphy));
1590fca65c1SSujith 	ret = ath_register_led(sc, &sc->assoc_led, trigger);
1600fca65c1SSujith 	sc->assoc_led.led_type = ATH_LED_ASSOC;
1610fca65c1SSujith 	if (ret)
1620fca65c1SSujith 		goto fail;
1630fca65c1SSujith 
1640fca65c1SSujith 	trigger = ieee80211_get_tx_led_name(sc->hw);
1650fca65c1SSujith 	snprintf(sc->tx_led.name, sizeof(sc->tx_led.name),
1660fca65c1SSujith 		"ath9k-%s::tx", wiphy_name(sc->hw->wiphy));
1670fca65c1SSujith 	ret = ath_register_led(sc, &sc->tx_led, trigger);
1680fca65c1SSujith 	sc->tx_led.led_type = ATH_LED_TX;
1690fca65c1SSujith 	if (ret)
1700fca65c1SSujith 		goto fail;
1710fca65c1SSujith 
1720fca65c1SSujith 	trigger = ieee80211_get_rx_led_name(sc->hw);
1730fca65c1SSujith 	snprintf(sc->rx_led.name, sizeof(sc->rx_led.name),
1740fca65c1SSujith 		"ath9k-%s::rx", wiphy_name(sc->hw->wiphy));
1750fca65c1SSujith 	ret = ath_register_led(sc, &sc->rx_led, trigger);
1760fca65c1SSujith 	sc->rx_led.led_type = ATH_LED_RX;
1770fca65c1SSujith 	if (ret)
1780fca65c1SSujith 		goto fail;
1790fca65c1SSujith 
1800fca65c1SSujith 	return;
1810fca65c1SSujith 
1820fca65c1SSujith fail:
1830fca65c1SSujith 	cancel_delayed_work_sync(&sc->ath_led_blink_work);
1840fca65c1SSujith 	ath_deinit_leds(sc);
1850fca65c1SSujith }
1860fca65c1SSujith 
1870fca65c1SSujith /*******************/
1880fca65c1SSujith /*	Rfkill	   */
1890fca65c1SSujith /*******************/
1900fca65c1SSujith 
1910fca65c1SSujith static bool ath_is_rfkill_set(struct ath_softc *sc)
1920fca65c1SSujith {
1930fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
1940fca65c1SSujith 
1950fca65c1SSujith 	return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
1960fca65c1SSujith 				  ah->rfkill_polarity;
1970fca65c1SSujith }
1980fca65c1SSujith 
1990fca65c1SSujith void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
2000fca65c1SSujith {
2010fca65c1SSujith 	struct ath_wiphy *aphy = hw->priv;
2020fca65c1SSujith 	struct ath_softc *sc = aphy->sc;
2030fca65c1SSujith 	bool blocked = !!ath_is_rfkill_set(sc);
2040fca65c1SSujith 
2050fca65c1SSujith 	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
2060fca65c1SSujith }
2070fca65c1SSujith 
2080fca65c1SSujith void ath_start_rfkill_poll(struct ath_softc *sc)
2090fca65c1SSujith {
2100fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
2110fca65c1SSujith 
2120fca65c1SSujith 	if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
2130fca65c1SSujith 		wiphy_rfkill_start_polling(sc->hw->wiphy);
2140fca65c1SSujith }
2150fca65c1SSujith 
2160fca65c1SSujith /******************/
2170fca65c1SSujith /*     BTCOEX     */
2180fca65c1SSujith /******************/
2190fca65c1SSujith 
2200fca65c1SSujith /*
2210fca65c1SSujith  * Detects if there is any priority bt traffic
2220fca65c1SSujith  */
2230fca65c1SSujith static void ath_detect_bt_priority(struct ath_softc *sc)
2240fca65c1SSujith {
2250fca65c1SSujith 	struct ath_btcoex *btcoex = &sc->btcoex;
2260fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
2270fca65c1SSujith 
2280fca65c1SSujith 	if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
2290fca65c1SSujith 		btcoex->bt_priority_cnt++;
2300fca65c1SSujith 
2310fca65c1SSujith 	if (time_after(jiffies, btcoex->bt_priority_time +
2320fca65c1SSujith 			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
233*58da1318SVasanthakumar Thiagarajan 		sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
234*58da1318SVasanthakumar Thiagarajan 		/* Detect if colocated bt started scanning */
235*58da1318SVasanthakumar Thiagarajan 		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
236*58da1318SVasanthakumar Thiagarajan 			ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
237*58da1318SVasanthakumar Thiagarajan 				  "BT scan detected");
238*58da1318SVasanthakumar Thiagarajan 			sc->sc_flags |= (SC_OP_BT_SCAN |
239*58da1318SVasanthakumar Thiagarajan 					 SC_OP_BT_PRIORITY_DETECTED);
240*58da1318SVasanthakumar Thiagarajan 		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
2410fca65c1SSujith 			ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
2420fca65c1SSujith 				  "BT priority traffic detected");
2430fca65c1SSujith 			sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
2440fca65c1SSujith 		}
2450fca65c1SSujith 
2460fca65c1SSujith 		btcoex->bt_priority_cnt = 0;
2470fca65c1SSujith 		btcoex->bt_priority_time = jiffies;
2480fca65c1SSujith 	}
2490fca65c1SSujith }
2500fca65c1SSujith 
2510fca65c1SSujith /*
2520fca65c1SSujith  * Configures appropriate weight based on stomp type.
2530fca65c1SSujith  */
2540fca65c1SSujith static void ath9k_btcoex_bt_stomp(struct ath_softc *sc,
2550fca65c1SSujith 				  enum ath_stomp_type stomp_type)
2560fca65c1SSujith {
2570fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
2580fca65c1SSujith 
2590fca65c1SSujith 	switch (stomp_type) {
2600fca65c1SSujith 	case ATH_BTCOEX_STOMP_ALL:
2610fca65c1SSujith 		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
2620fca65c1SSujith 					   AR_STOMP_ALL_WLAN_WGHT);
2630fca65c1SSujith 		break;
2640fca65c1SSujith 	case ATH_BTCOEX_STOMP_LOW:
2650fca65c1SSujith 		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
2660fca65c1SSujith 					   AR_STOMP_LOW_WLAN_WGHT);
2670fca65c1SSujith 		break;
2680fca65c1SSujith 	case ATH_BTCOEX_STOMP_NONE:
2690fca65c1SSujith 		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
2700fca65c1SSujith 					   AR_STOMP_NONE_WLAN_WGHT);
2710fca65c1SSujith 		break;
2720fca65c1SSujith 	default:
2730fca65c1SSujith 		ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
2740fca65c1SSujith 			  "Invalid Stomptype\n");
2750fca65c1SSujith 		break;
2760fca65c1SSujith 	}
2770fca65c1SSujith 
2780fca65c1SSujith 	ath9k_hw_btcoex_enable(ah);
2790fca65c1SSujith }
2800fca65c1SSujith 
2810fca65c1SSujith static void ath9k_gen_timer_start(struct ath_hw *ah,
2820fca65c1SSujith 				  struct ath_gen_timer *timer,
2830fca65c1SSujith 				  u32 timer_next,
2840fca65c1SSujith 				  u32 timer_period)
2850fca65c1SSujith {
2860fca65c1SSujith 	struct ath_common *common = ath9k_hw_common(ah);
2870fca65c1SSujith 	struct ath_softc *sc = (struct ath_softc *) common->priv;
2880fca65c1SSujith 
2890fca65c1SSujith 	ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
2900fca65c1SSujith 
2910fca65c1SSujith 	if ((sc->imask & ATH9K_INT_GENTIMER) == 0) {
2920fca65c1SSujith 		ath9k_hw_set_interrupts(ah, 0);
2930fca65c1SSujith 		sc->imask |= ATH9K_INT_GENTIMER;
2940fca65c1SSujith 		ath9k_hw_set_interrupts(ah, sc->imask);
2950fca65c1SSujith 	}
2960fca65c1SSujith }
2970fca65c1SSujith 
2980fca65c1SSujith static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
2990fca65c1SSujith {
3000fca65c1SSujith 	struct ath_common *common = ath9k_hw_common(ah);
3010fca65c1SSujith 	struct ath_softc *sc = (struct ath_softc *) common->priv;
3020fca65c1SSujith 	struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
3030fca65c1SSujith 
3040fca65c1SSujith 	ath9k_hw_gen_timer_stop(ah, timer);
3050fca65c1SSujith 
3060fca65c1SSujith 	/* if no timer is enabled, turn off interrupt mask */
3070fca65c1SSujith 	if (timer_table->timer_mask.val == 0) {
3080fca65c1SSujith 		ath9k_hw_set_interrupts(ah, 0);
3090fca65c1SSujith 		sc->imask &= ~ATH9K_INT_GENTIMER;
3100fca65c1SSujith 		ath9k_hw_set_interrupts(ah, sc->imask);
3110fca65c1SSujith 	}
3120fca65c1SSujith }
3130fca65c1SSujith 
3140fca65c1SSujith /*
3150fca65c1SSujith  * This is the master bt coex timer which runs for every
3160fca65c1SSujith  * 45ms, bt traffic will be given priority during 55% of this
3170fca65c1SSujith  * period while wlan gets remaining 45%
3180fca65c1SSujith  */
3190fca65c1SSujith static void ath_btcoex_period_timer(unsigned long data)
3200fca65c1SSujith {
3210fca65c1SSujith 	struct ath_softc *sc = (struct ath_softc *) data;
3220fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
3230fca65c1SSujith 	struct ath_btcoex *btcoex = &sc->btcoex;
324*58da1318SVasanthakumar Thiagarajan 	u32 timer_period;
325*58da1318SVasanthakumar Thiagarajan 	bool is_btscan;
3260fca65c1SSujith 
3270fca65c1SSujith 	ath_detect_bt_priority(sc);
3280fca65c1SSujith 
329*58da1318SVasanthakumar Thiagarajan 	is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
330*58da1318SVasanthakumar Thiagarajan 
3310fca65c1SSujith 	spin_lock_bh(&btcoex->btcoex_lock);
3320fca65c1SSujith 
333*58da1318SVasanthakumar Thiagarajan 	ath9k_btcoex_bt_stomp(sc, is_btscan ? ATH_BTCOEX_STOMP_ALL :
334*58da1318SVasanthakumar Thiagarajan 			      btcoex->bt_stomp_type);
3350fca65c1SSujith 
3360fca65c1SSujith 	spin_unlock_bh(&btcoex->btcoex_lock);
3370fca65c1SSujith 
3380fca65c1SSujith 	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
3390fca65c1SSujith 		if (btcoex->hw_timer_enabled)
3400fca65c1SSujith 			ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
3410fca65c1SSujith 
342*58da1318SVasanthakumar Thiagarajan 		timer_period = is_btscan ? btcoex->btscan_no_stomp :
343*58da1318SVasanthakumar Thiagarajan 					   btcoex->btcoex_no_stomp;
3440fca65c1SSujith 		ath9k_gen_timer_start(ah,
3450fca65c1SSujith 				      btcoex->no_stomp_timer,
3460fca65c1SSujith 				      (ath9k_hw_gettsf32(ah) +
347*58da1318SVasanthakumar Thiagarajan 				       timer_period), timer_period * 10);
3480fca65c1SSujith 		btcoex->hw_timer_enabled = true;
3490fca65c1SSujith 	}
3500fca65c1SSujith 
3510fca65c1SSujith 	mod_timer(&btcoex->period_timer, jiffies +
3520fca65c1SSujith 				  msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
3530fca65c1SSujith }
3540fca65c1SSujith 
3550fca65c1SSujith /*
3560fca65c1SSujith  * Generic tsf based hw timer which configures weight
3570fca65c1SSujith  * registers to time slice between wlan and bt traffic
3580fca65c1SSujith  */
3590fca65c1SSujith static void ath_btcoex_no_stomp_timer(void *arg)
3600fca65c1SSujith {
3610fca65c1SSujith 	struct ath_softc *sc = (struct ath_softc *)arg;
3620fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
3630fca65c1SSujith 	struct ath_btcoex *btcoex = &sc->btcoex;
364*58da1318SVasanthakumar Thiagarajan 	bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
3650fca65c1SSujith 
3660fca65c1SSujith 	ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
3670fca65c1SSujith 		  "no stomp timer running \n");
3680fca65c1SSujith 
3690fca65c1SSujith 	spin_lock_bh(&btcoex->btcoex_lock);
3700fca65c1SSujith 
371*58da1318SVasanthakumar Thiagarajan 	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
3720fca65c1SSujith 		ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE);
3730fca65c1SSujith 	 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
3740fca65c1SSujith 		ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW);
3750fca65c1SSujith 
3760fca65c1SSujith 	spin_unlock_bh(&btcoex->btcoex_lock);
3770fca65c1SSujith }
3780fca65c1SSujith 
3790fca65c1SSujith int ath_init_btcoex_timer(struct ath_softc *sc)
3800fca65c1SSujith {
3810fca65c1SSujith 	struct ath_btcoex *btcoex = &sc->btcoex;
3820fca65c1SSujith 
3830fca65c1SSujith 	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
3840fca65c1SSujith 	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
3850fca65c1SSujith 		btcoex->btcoex_period / 100;
386*58da1318SVasanthakumar Thiagarajan 	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
387*58da1318SVasanthakumar Thiagarajan 				   btcoex->btcoex_period / 100;
3880fca65c1SSujith 
3890fca65c1SSujith 	setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
3900fca65c1SSujith 			(unsigned long) sc);
3910fca65c1SSujith 
3920fca65c1SSujith 	spin_lock_init(&btcoex->btcoex_lock);
3930fca65c1SSujith 
3940fca65c1SSujith 	btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
3950fca65c1SSujith 			ath_btcoex_no_stomp_timer,
3960fca65c1SSujith 			ath_btcoex_no_stomp_timer,
3970fca65c1SSujith 			(void *) sc, AR_FIRST_NDP_TIMER);
3980fca65c1SSujith 
3990fca65c1SSujith 	if (!btcoex->no_stomp_timer)
4000fca65c1SSujith 		return -ENOMEM;
4010fca65c1SSujith 
4020fca65c1SSujith 	return 0;
4030fca65c1SSujith }
4040fca65c1SSujith 
4050fca65c1SSujith /*
4060fca65c1SSujith  * (Re)start btcoex timers
4070fca65c1SSujith  */
4080fca65c1SSujith void ath9k_btcoex_timer_resume(struct ath_softc *sc)
4090fca65c1SSujith {
4100fca65c1SSujith 	struct ath_btcoex *btcoex = &sc->btcoex;
4110fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
4120fca65c1SSujith 
4130fca65c1SSujith 	ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
4140fca65c1SSujith 		  "Starting btcoex timers");
4150fca65c1SSujith 
4160fca65c1SSujith 	/* make sure duty cycle timer is also stopped when resuming */
4170fca65c1SSujith 	if (btcoex->hw_timer_enabled)
4180fca65c1SSujith 		ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
4190fca65c1SSujith 
4200fca65c1SSujith 	btcoex->bt_priority_cnt = 0;
4210fca65c1SSujith 	btcoex->bt_priority_time = jiffies;
422*58da1318SVasanthakumar Thiagarajan 	sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
4230fca65c1SSujith 
4240fca65c1SSujith 	mod_timer(&btcoex->period_timer, jiffies);
4250fca65c1SSujith }
4260fca65c1SSujith 
4270fca65c1SSujith 
4280fca65c1SSujith /*
4290fca65c1SSujith  * Pause btcoex timer and bt duty cycle timer
4300fca65c1SSujith  */
4310fca65c1SSujith void ath9k_btcoex_timer_pause(struct ath_softc *sc)
4320fca65c1SSujith {
4330fca65c1SSujith 	struct ath_btcoex *btcoex = &sc->btcoex;
4340fca65c1SSujith 	struct ath_hw *ah = sc->sc_ah;
4350fca65c1SSujith 
4360fca65c1SSujith 	del_timer_sync(&btcoex->period_timer);
4370fca65c1SSujith 
4380fca65c1SSujith 	if (btcoex->hw_timer_enabled)
4390fca65c1SSujith 		ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
4400fca65c1SSujith 
4410fca65c1SSujith 	btcoex->hw_timer_enabled = false;
4420fca65c1SSujith }
443