162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "ath9k.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/********************************/
2062306a36Sopenharmony_ci/*	 LED functions		*/
2162306a36Sopenharmony_ci/********************************/
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_LEDS
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void ath_fill_led_pin(struct ath_softc *sc)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/* Set default led pin if invalid */
3062306a36Sopenharmony_ci	if (ah->led_pin < 0) {
3162306a36Sopenharmony_ci		if (AR_SREV_9287(ah))
3262306a36Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9287;
3362306a36Sopenharmony_ci		else if (AR_SREV_9485(ah))
3462306a36Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9485;
3562306a36Sopenharmony_ci		else if (AR_SREV_9300(ah))
3662306a36Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9300;
3762306a36Sopenharmony_ci		else if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
3862306a36Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9462;
3962306a36Sopenharmony_ci		else
4062306a36Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_DEF;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Configure gpio for output */
4462306a36Sopenharmony_ci	ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
4562306a36Sopenharmony_ci				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* LED off, active low */
4862306a36Sopenharmony_ci	ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void ath_led_brightness(struct led_classdev *led_cdev,
5262306a36Sopenharmony_ci			       enum led_brightness brightness)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
5562306a36Sopenharmony_ci	u32 val = (brightness == LED_OFF);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (sc->sc_ah->config.led_active_high)
5862306a36Sopenharmony_ci		val = !val;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid ath_deinit_leds(struct ath_softc *sc)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	if (!sc->led_registered)
6662306a36Sopenharmony_ci		return;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ath_led_brightness(&sc->led_cdev, LED_OFF);
6962306a36Sopenharmony_ci	led_classdev_unregister(&sc->led_cdev);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_civoid ath_init_leds(struct ath_softc *sc)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int ret;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (AR_SREV_9100(sc->sc_ah))
7962306a36Sopenharmony_ci		return;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ath_fill_led_pin(sc);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!ath9k_led_blink)
8462306a36Sopenharmony_ci		sc->led_cdev.default_trigger =
8562306a36Sopenharmony_ci			ieee80211_get_radio_led_name(sc->hw);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	snprintf(sc->led_name, sizeof(sc->led_name),
8862306a36Sopenharmony_ci		"ath9k-%s", wiphy_name(sc->hw->wiphy));
8962306a36Sopenharmony_ci	sc->led_cdev.name = sc->led_name;
9062306a36Sopenharmony_ci	sc->led_cdev.brightness_set = ath_led_brightness;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
9362306a36Sopenharmony_ci	if (ret < 0)
9462306a36Sopenharmony_ci		return;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	sc->led_registered = true;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci#endif
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*******************/
10162306a36Sopenharmony_ci/*	Rfkill	   */
10262306a36Sopenharmony_ci/*******************/
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic bool ath_is_rfkill_set(struct ath_softc *sc)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
10762306a36Sopenharmony_ci	bool is_blocked;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
11062306a36Sopenharmony_ci	is_blocked = ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
11162306a36Sopenharmony_ci				  ah->rfkill_polarity;
11262306a36Sopenharmony_ci	ath9k_ps_restore(sc);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return is_blocked;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_civoid ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct ath_softc *sc = hw->priv;
12062306a36Sopenharmony_ci	bool blocked = !!ath_is_rfkill_set(sc);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_civoid ath_start_rfkill_poll(struct ath_softc *sc)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
13062306a36Sopenharmony_ci		wiphy_rfkill_start_polling(sc->hw->wiphy);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/******************/
13662306a36Sopenharmony_ci/*     BTCOEX     */
13762306a36Sopenharmony_ci/******************/
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * Detects if there is any priority bt traffic
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_cistatic void ath_detect_bt_priority(struct ath_softc *sc)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
14562306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
14862306a36Sopenharmony_ci		btcoex->bt_priority_cnt++;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (time_after(jiffies, btcoex->bt_priority_time +
15162306a36Sopenharmony_ci			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
15262306a36Sopenharmony_ci		clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
15362306a36Sopenharmony_ci		clear_bit(BT_OP_SCAN, &btcoex->op_flags);
15462306a36Sopenharmony_ci		/* Detect if colocated bt started scanning */
15562306a36Sopenharmony_ci		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
15662306a36Sopenharmony_ci			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
15762306a36Sopenharmony_ci				"BT scan detected\n");
15862306a36Sopenharmony_ci			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
15962306a36Sopenharmony_ci			set_bit(BT_OP_SCAN, &btcoex->op_flags);
16062306a36Sopenharmony_ci		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
16162306a36Sopenharmony_ci			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
16262306a36Sopenharmony_ci				"BT priority traffic detected\n");
16362306a36Sopenharmony_ci			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		btcoex->bt_priority_cnt = 0;
16762306a36Sopenharmony_ci		btcoex->bt_priority_time = jiffies;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void ath_mci_ftp_adjust(struct ath_softc *sc)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
17462306a36Sopenharmony_ci	struct ath_mci_profile *mci = &btcoex->mci;
17562306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
17862306a36Sopenharmony_ci		if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) &&
17962306a36Sopenharmony_ci		    (mci->num_pan || mci->num_other_acl))
18062306a36Sopenharmony_ci			ah->btcoex_hw.mci.stomp_ftp =
18162306a36Sopenharmony_ci				(sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
18262306a36Sopenharmony_ci		else
18362306a36Sopenharmony_ci			ah->btcoex_hw.mci.stomp_ftp = false;
18462306a36Sopenharmony_ci		btcoex->bt_wait_time = 0;
18562306a36Sopenharmony_ci		sc->rx.num_pkts = 0;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/*
19062306a36Sopenharmony_ci * This is the master bt coex timer which runs for every
19162306a36Sopenharmony_ci * 45ms, bt traffic will be given priority during 55% of this
19262306a36Sopenharmony_ci * period while wlan gets remaining 45%
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_cistatic void ath_btcoex_period_timer(struct timer_list *t)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct ath_softc *sc = from_timer(sc, t, btcoex.period_timer);
19762306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
19862306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
19962306a36Sopenharmony_ci	enum ath_stomp_type stomp_type;
20062306a36Sopenharmony_ci	u32 timer_period;
20162306a36Sopenharmony_ci	unsigned long flags;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	spin_lock_irqsave(&sc->sc_pm_lock, flags);
20462306a36Sopenharmony_ci	if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) {
20562306a36Sopenharmony_ci		btcoex->bt_wait_time += btcoex->btcoex_period;
20662306a36Sopenharmony_ci		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
20762306a36Sopenharmony_ci		goto skip_hw_wakeup;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
21262306a36Sopenharmony_ci	spin_lock_bh(&btcoex->btcoex_lock);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) {
21562306a36Sopenharmony_ci		ath9k_mci_update_rssi(sc);
21662306a36Sopenharmony_ci		ath_mci_ftp_adjust(sc);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
22062306a36Sopenharmony_ci		ath_detect_bt_priority(sc);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	stomp_type = btcoex->bt_stomp_type;
22362306a36Sopenharmony_ci	timer_period = btcoex->btcoex_no_stomp;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) {
22662306a36Sopenharmony_ci		if (test_bit(BT_OP_SCAN, &btcoex->op_flags)) {
22762306a36Sopenharmony_ci			stomp_type = ATH_BTCOEX_STOMP_ALL;
22862306a36Sopenharmony_ci			timer_period = btcoex->btscan_no_stomp;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci	} else if (btcoex->stomp_audio >= 5) {
23162306a36Sopenharmony_ci		stomp_type = ATH_BTCOEX_STOMP_AUDIO;
23262306a36Sopenharmony_ci		btcoex->stomp_audio = 0;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ath9k_hw_btcoex_bt_stomp(ah, stomp_type);
23662306a36Sopenharmony_ci	ath9k_hw_btcoex_enable(ah);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	spin_unlock_bh(&btcoex->btcoex_lock);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp)
24162306a36Sopenharmony_ci		mod_timer(&btcoex->no_stomp_timer,
24262306a36Sopenharmony_ci			 jiffies + msecs_to_jiffies(timer_period));
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ath9k_ps_restore(sc);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciskip_hw_wakeup:
24762306a36Sopenharmony_ci	mod_timer(&btcoex->period_timer,
24862306a36Sopenharmony_ci		  jiffies + msecs_to_jiffies(btcoex->btcoex_period));
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/*
25262306a36Sopenharmony_ci * Generic tsf based hw timer which configures weight
25362306a36Sopenharmony_ci * registers to time slice between wlan and bt traffic
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_cistatic void ath_btcoex_no_stomp_timer(struct timer_list *t)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct ath_softc *sc = from_timer(sc, t, btcoex.no_stomp_timer);
25862306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
25962306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
26262306a36Sopenharmony_ci	spin_lock_bh(&btcoex->btcoex_lock);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW ||
26562306a36Sopenharmony_ci	    (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) &&
26662306a36Sopenharmony_ci	     test_bit(BT_OP_SCAN, &btcoex->op_flags)))
26762306a36Sopenharmony_ci		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
26862306a36Sopenharmony_ci	else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
26962306a36Sopenharmony_ci		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ath9k_hw_btcoex_enable(ah);
27262306a36Sopenharmony_ci	spin_unlock_bh(&btcoex->btcoex_lock);
27362306a36Sopenharmony_ci	ath9k_ps_restore(sc);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic void ath_init_btcoex_timer(struct ath_softc *sc)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
28162306a36Sopenharmony_ci	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
28262306a36Sopenharmony_ci		btcoex->btcoex_period / 100;
28362306a36Sopenharmony_ci	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
28462306a36Sopenharmony_ci				   btcoex->btcoex_period / 100;
28562306a36Sopenharmony_ci	btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	timer_setup(&btcoex->period_timer, ath_btcoex_period_timer, 0);
28862306a36Sopenharmony_ci	timer_setup(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer, 0);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	spin_lock_init(&btcoex->btcoex_lock);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/*
29462306a36Sopenharmony_ci * (Re)start btcoex timers
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_civoid ath9k_btcoex_timer_resume(struct ath_softc *sc)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
29962306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
30262306a36Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
30362306a36Sopenharmony_ci		return;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* make sure duty cycle timer is also stopped when resuming */
30862306a36Sopenharmony_ci	del_timer_sync(&btcoex->no_stomp_timer);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	btcoex->bt_priority_cnt = 0;
31162306a36Sopenharmony_ci	btcoex->bt_priority_time = jiffies;
31262306a36Sopenharmony_ci	clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
31362306a36Sopenharmony_ci	clear_bit(BT_OP_SCAN, &btcoex->op_flags);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	mod_timer(&btcoex->period_timer, jiffies);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/*
31962306a36Sopenharmony_ci * Pause btcoex timer and bt duty cycle timer
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_civoid ath9k_btcoex_timer_pause(struct ath_softc *sc)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
32462306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
32762306a36Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
32862306a36Sopenharmony_ci		return;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n");
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	del_timer_sync(&btcoex->period_timer);
33362306a36Sopenharmony_ci	del_timer_sync(&btcoex->no_stomp_timer);
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_civoid ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	del_timer_sync(&btcoex->no_stomp_timer);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ciu16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
34662306a36Sopenharmony_ci	struct ath_mci_profile *mci = &sc->btcoex.mci;
34762306a36Sopenharmony_ci	u16 aggr_limit = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit)
35062306a36Sopenharmony_ci		aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4;
35162306a36Sopenharmony_ci	else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags))
35262306a36Sopenharmony_ci		aggr_limit = min((max_4ms_framelen * 3) / 8,
35362306a36Sopenharmony_ci				 (u32)ATH_AMPDU_LIMIT_MAX);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return aggr_limit;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_civoid ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	if (status & ATH9K_INT_MCI)
36162306a36Sopenharmony_ci		ath_mci_intr(sc);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_civoid ath9k_start_btcoex(struct ath_softc *sc)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (ah->btcoex_hw.enabled ||
36962306a36Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
37062306a36Sopenharmony_ci		return;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
37362306a36Sopenharmony_ci		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
37462306a36Sopenharmony_ci					   AR_STOMP_LOW_WLAN_WGHT, 0);
37562306a36Sopenharmony_ci	else
37662306a36Sopenharmony_ci		ath9k_hw_btcoex_set_weight(ah, 0, 0,
37762306a36Sopenharmony_ci					   ATH_BTCOEX_STOMP_NONE);
37862306a36Sopenharmony_ci	ath9k_hw_btcoex_enable(ah);
37962306a36Sopenharmony_ci	ath9k_btcoex_timer_resume(sc);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_civoid ath9k_stop_btcoex(struct ath_softc *sc)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!ah->btcoex_hw.enabled ||
38762306a36Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
38862306a36Sopenharmony_ci		return;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ath9k_btcoex_timer_pause(sc);
39162306a36Sopenharmony_ci	ath9k_hw_btcoex_disable(ah);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
39462306a36Sopenharmony_ci		ath_mci_flush_profile(&sc->btcoex.mci);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_civoid ath9k_deinit_btcoex(struct ath_softc *sc)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (ath9k_hw_mci_is_enabled(ah))
40262306a36Sopenharmony_ci		ath_mci_cleanup(sc);
40362306a36Sopenharmony_ci	else {
40462306a36Sopenharmony_ci		enum ath_btcoex_scheme scheme = ath9k_hw_get_btcoex_scheme(ah);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		if (scheme == ATH_BTCOEX_CFG_2WIRE ||
40762306a36Sopenharmony_ci		    scheme == ATH_BTCOEX_CFG_3WIRE)
40862306a36Sopenharmony_ci			ath9k_hw_btcoex_deinit(sc->sc_ah);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciint ath9k_init_btcoex(struct ath_softc *sc)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct ath_txq *txq;
41562306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
41662306a36Sopenharmony_ci	int r;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ath9k_hw_btcoex_init_scheme(ah);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	switch (ath9k_hw_get_btcoex_scheme(sc->sc_ah)) {
42162306a36Sopenharmony_ci	case ATH_BTCOEX_CFG_NONE:
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci	case ATH_BTCOEX_CFG_2WIRE:
42462306a36Sopenharmony_ci		ath9k_hw_btcoex_init_2wire(sc->sc_ah);
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci	case ATH_BTCOEX_CFG_3WIRE:
42762306a36Sopenharmony_ci		ath9k_hw_btcoex_init_3wire(sc->sc_ah);
42862306a36Sopenharmony_ci		ath_init_btcoex_timer(sc);
42962306a36Sopenharmony_ci		txq = sc->tx.txq_map[IEEE80211_AC_BE];
43062306a36Sopenharmony_ci		ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum);
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	case ATH_BTCOEX_CFG_MCI:
43362306a36Sopenharmony_ci		ath_init_btcoex_timer(sc);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
43662306a36Sopenharmony_ci		INIT_LIST_HEAD(&sc->btcoex.mci.info);
43762306a36Sopenharmony_ci		ath9k_hw_btcoex_init_mci(ah);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		r = ath_mci_setup(sc);
44062306a36Sopenharmony_ci		if (r)
44162306a36Sopenharmony_ci			return r;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	default:
44562306a36Sopenharmony_ci		WARN_ON(1);
44662306a36Sopenharmony_ci		break;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
45562306a36Sopenharmony_ci	struct ath_mci_profile *mci = &btcoex->mci;
45662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
45762306a36Sopenharmony_ci	struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
45862306a36Sopenharmony_ci	u32 len = 0;
45962306a36Sopenharmony_ci	int i;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Total BT profiles", NUM_PROF(mci));
46262306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("MGMT", mci->num_mgmt);
46362306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("SCO", mci->num_sco);
46462306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("A2DP", mci->num_a2dp);
46562306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("HID", mci->num_hid);
46662306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("PAN", mci->num_pan);
46762306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("ACL", mci->num_other_acl);
46862306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("BDR", mci->num_bdr);
46962306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Aggr. Limit", mci->aggr_limit);
47062306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type);
47162306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period);
47262306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle);
47362306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time);
47462306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx);
47562306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	len += scnprintf(buf + len, size - len, "BT Weights: ");
47862306a36Sopenharmony_ci	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
47962306a36Sopenharmony_ci		len += scnprintf(buf + len, size - len, "%08x ",
48062306a36Sopenharmony_ci				 btcoex_hw->bt_weight[i]);
48162306a36Sopenharmony_ci	len += scnprintf(buf + len, size - len, "\n");
48262306a36Sopenharmony_ci	len += scnprintf(buf + len, size - len, "WLAN Weights: ");
48362306a36Sopenharmony_ci	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
48462306a36Sopenharmony_ci		len += scnprintf(buf + len, size - len, "%08x ",
48562306a36Sopenharmony_ci				 btcoex_hw->wlan_weight[i]);
48662306a36Sopenharmony_ci	len += scnprintf(buf + len, size - len, "\n");
48762306a36Sopenharmony_ci	len += scnprintf(buf + len, size - len, "Tx Priorities: ");
48862306a36Sopenharmony_ci	for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++)
48962306a36Sopenharmony_ci		len += scnprintf(buf + len, size - len, "%08x ",
49062306a36Sopenharmony_ci				btcoex_hw->tx_prio[i]);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	len += scnprintf(buf + len, size - len, "\n");
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return len;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int ath9k_dump_legacy_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
50162306a36Sopenharmony_ci	u32 len = 0;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type);
50462306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period);
50562306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle);
50662306a36Sopenharmony_ci	ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return len;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ciint ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	if (ath9k_hw_mci_is_enabled(sc->sc_ah))
51462306a36Sopenharmony_ci		return ath9k_dump_mci_btcoex(sc, buf, size);
51562306a36Sopenharmony_ci	else
51662306a36Sopenharmony_ci		return ath9k_dump_legacy_btcoex(sc, buf, size);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
520