18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "ath9k.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/********************************/
208c2ecf20Sopenharmony_ci/*	 LED functions		*/
218c2ecf20Sopenharmony_ci/********************************/
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_LEDS
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void ath_fill_led_pin(struct ath_softc *sc)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* Set default led pin if invalid */
308c2ecf20Sopenharmony_ci	if (ah->led_pin < 0) {
318c2ecf20Sopenharmony_ci		if (AR_SREV_9287(ah))
328c2ecf20Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9287;
338c2ecf20Sopenharmony_ci		else if (AR_SREV_9485(ah))
348c2ecf20Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9485;
358c2ecf20Sopenharmony_ci		else if (AR_SREV_9300(ah))
368c2ecf20Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9300;
378c2ecf20Sopenharmony_ci		else if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
388c2ecf20Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_9462;
398c2ecf20Sopenharmony_ci		else
408c2ecf20Sopenharmony_ci			ah->led_pin = ATH_LED_PIN_DEF;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Configure gpio for output */
448c2ecf20Sopenharmony_ci	ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
458c2ecf20Sopenharmony_ci				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* LED off, active low */
488c2ecf20Sopenharmony_ci	ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void ath_led_brightness(struct led_classdev *led_cdev,
528c2ecf20Sopenharmony_ci			       enum led_brightness brightness)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
558c2ecf20Sopenharmony_ci	u32 val = (brightness == LED_OFF);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (sc->sc_ah->config.led_active_high)
588c2ecf20Sopenharmony_ci		val = !val;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_civoid ath_deinit_leds(struct ath_softc *sc)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	if (!sc->led_registered)
668c2ecf20Sopenharmony_ci		return;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	ath_led_brightness(&sc->led_cdev, LED_OFF);
698c2ecf20Sopenharmony_ci	led_classdev_unregister(&sc->led_cdev);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_civoid ath_init_leds(struct ath_softc *sc)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (AR_SREV_9100(sc->sc_ah))
798c2ecf20Sopenharmony_ci		return;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ath_fill_led_pin(sc);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (!ath9k_led_blink)
848c2ecf20Sopenharmony_ci		sc->led_cdev.default_trigger =
858c2ecf20Sopenharmony_ci			ieee80211_get_radio_led_name(sc->hw);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	snprintf(sc->led_name, sizeof(sc->led_name),
888c2ecf20Sopenharmony_ci		"ath9k-%s", wiphy_name(sc->hw->wiphy));
898c2ecf20Sopenharmony_ci	sc->led_cdev.name = sc->led_name;
908c2ecf20Sopenharmony_ci	sc->led_cdev.brightness_set = ath_led_brightness;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
938c2ecf20Sopenharmony_ci	if (ret < 0)
948c2ecf20Sopenharmony_ci		return;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	sc->led_registered = true;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci#endif
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/*******************/
1018c2ecf20Sopenharmony_ci/*	Rfkill	   */
1028c2ecf20Sopenharmony_ci/*******************/
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic bool ath_is_rfkill_set(struct ath_softc *sc)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1078c2ecf20Sopenharmony_ci	bool is_blocked;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	ath9k_ps_wakeup(sc);
1108c2ecf20Sopenharmony_ci	is_blocked = ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
1118c2ecf20Sopenharmony_ci				  ah->rfkill_polarity;
1128c2ecf20Sopenharmony_ci	ath9k_ps_restore(sc);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return is_blocked;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_civoid ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
1208c2ecf20Sopenharmony_ci	bool blocked = !!ath_is_rfkill_set(sc);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_civoid ath_start_rfkill_poll(struct ath_softc *sc)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1308c2ecf20Sopenharmony_ci		wiphy_rfkill_start_polling(sc->hw->wiphy);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/******************/
1368c2ecf20Sopenharmony_ci/*     BTCOEX     */
1378c2ecf20Sopenharmony_ci/******************/
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * Detects if there is any priority bt traffic
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_cistatic void ath_detect_bt_priority(struct ath_softc *sc)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
1458c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
1488c2ecf20Sopenharmony_ci		btcoex->bt_priority_cnt++;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (time_after(jiffies, btcoex->bt_priority_time +
1518c2ecf20Sopenharmony_ci			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
1528c2ecf20Sopenharmony_ci		clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
1538c2ecf20Sopenharmony_ci		clear_bit(BT_OP_SCAN, &btcoex->op_flags);
1548c2ecf20Sopenharmony_ci		/* Detect if colocated bt started scanning */
1558c2ecf20Sopenharmony_ci		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
1568c2ecf20Sopenharmony_ci			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
1578c2ecf20Sopenharmony_ci				"BT scan detected\n");
1588c2ecf20Sopenharmony_ci			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
1598c2ecf20Sopenharmony_ci			set_bit(BT_OP_SCAN, &btcoex->op_flags);
1608c2ecf20Sopenharmony_ci		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
1618c2ecf20Sopenharmony_ci			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
1628c2ecf20Sopenharmony_ci				"BT priority traffic detected\n");
1638c2ecf20Sopenharmony_ci			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		btcoex->bt_priority_cnt = 0;
1678c2ecf20Sopenharmony_ci		btcoex->bt_priority_time = jiffies;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void ath_mci_ftp_adjust(struct ath_softc *sc)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
1748c2ecf20Sopenharmony_ci	struct ath_mci_profile *mci = &btcoex->mci;
1758c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
1788c2ecf20Sopenharmony_ci		if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) &&
1798c2ecf20Sopenharmony_ci		    (mci->num_pan || mci->num_other_acl))
1808c2ecf20Sopenharmony_ci			ah->btcoex_hw.mci.stomp_ftp =
1818c2ecf20Sopenharmony_ci				(sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
1828c2ecf20Sopenharmony_ci		else
1838c2ecf20Sopenharmony_ci			ah->btcoex_hw.mci.stomp_ftp = false;
1848c2ecf20Sopenharmony_ci		btcoex->bt_wait_time = 0;
1858c2ecf20Sopenharmony_ci		sc->rx.num_pkts = 0;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/*
1908c2ecf20Sopenharmony_ci * This is the master bt coex timer which runs for every
1918c2ecf20Sopenharmony_ci * 45ms, bt traffic will be given priority during 55% of this
1928c2ecf20Sopenharmony_ci * period while wlan gets remaining 45%
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic void ath_btcoex_period_timer(struct timer_list *t)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct ath_softc *sc = from_timer(sc, t, btcoex.period_timer);
1978c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1988c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
1998c2ecf20Sopenharmony_ci	enum ath_stomp_type stomp_type;
2008c2ecf20Sopenharmony_ci	u32 timer_period;
2018c2ecf20Sopenharmony_ci	unsigned long flags;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sc->sc_pm_lock, flags);
2048c2ecf20Sopenharmony_ci	if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) {
2058c2ecf20Sopenharmony_ci		btcoex->bt_wait_time += btcoex->btcoex_period;
2068c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
2078c2ecf20Sopenharmony_ci		goto skip_hw_wakeup;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	ath9k_ps_wakeup(sc);
2128c2ecf20Sopenharmony_ci	spin_lock_bh(&btcoex->btcoex_lock);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) {
2158c2ecf20Sopenharmony_ci		ath9k_mci_update_rssi(sc);
2168c2ecf20Sopenharmony_ci		ath_mci_ftp_adjust(sc);
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
2208c2ecf20Sopenharmony_ci		ath_detect_bt_priority(sc);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	stomp_type = btcoex->bt_stomp_type;
2238c2ecf20Sopenharmony_ci	timer_period = btcoex->btcoex_no_stomp;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) {
2268c2ecf20Sopenharmony_ci		if (test_bit(BT_OP_SCAN, &btcoex->op_flags)) {
2278c2ecf20Sopenharmony_ci			stomp_type = ATH_BTCOEX_STOMP_ALL;
2288c2ecf20Sopenharmony_ci			timer_period = btcoex->btscan_no_stomp;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci	} else if (btcoex->stomp_audio >= 5) {
2318c2ecf20Sopenharmony_ci		stomp_type = ATH_BTCOEX_STOMP_AUDIO;
2328c2ecf20Sopenharmony_ci		btcoex->stomp_audio = 0;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ath9k_hw_btcoex_bt_stomp(ah, stomp_type);
2368c2ecf20Sopenharmony_ci	ath9k_hw_btcoex_enable(ah);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	spin_unlock_bh(&btcoex->btcoex_lock);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp)
2418c2ecf20Sopenharmony_ci		mod_timer(&btcoex->no_stomp_timer,
2428c2ecf20Sopenharmony_ci			 jiffies + msecs_to_jiffies(timer_period));
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ath9k_ps_restore(sc);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciskip_hw_wakeup:
2478c2ecf20Sopenharmony_ci	mod_timer(&btcoex->period_timer,
2488c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(btcoex->btcoex_period));
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * Generic tsf based hw timer which configures weight
2538c2ecf20Sopenharmony_ci * registers to time slice between wlan and bt traffic
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_cistatic void ath_btcoex_no_stomp_timer(struct timer_list *t)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct ath_softc *sc = from_timer(sc, t, btcoex.no_stomp_timer);
2588c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
2598c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	ath9k_ps_wakeup(sc);
2628c2ecf20Sopenharmony_ci	spin_lock_bh(&btcoex->btcoex_lock);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW ||
2658c2ecf20Sopenharmony_ci	    (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) &&
2668c2ecf20Sopenharmony_ci	     test_bit(BT_OP_SCAN, &btcoex->op_flags)))
2678c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
2688c2ecf20Sopenharmony_ci	else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
2698c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	ath9k_hw_btcoex_enable(ah);
2728c2ecf20Sopenharmony_ci	spin_unlock_bh(&btcoex->btcoex_lock);
2738c2ecf20Sopenharmony_ci	ath9k_ps_restore(sc);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void ath_init_btcoex_timer(struct ath_softc *sc)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
2818c2ecf20Sopenharmony_ci	btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
2828c2ecf20Sopenharmony_ci		btcoex->btcoex_period / 100;
2838c2ecf20Sopenharmony_ci	btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
2848c2ecf20Sopenharmony_ci				   btcoex->btcoex_period / 100;
2858c2ecf20Sopenharmony_ci	btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	timer_setup(&btcoex->period_timer, ath_btcoex_period_timer, 0);
2888c2ecf20Sopenharmony_ci	timer_setup(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer, 0);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_lock_init(&btcoex->btcoex_lock);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/*
2948c2ecf20Sopenharmony_ci * (Re)start btcoex timers
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_civoid ath9k_btcoex_timer_resume(struct ath_softc *sc)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
2998c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
3028c2ecf20Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
3038c2ecf20Sopenharmony_ci		return;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* make sure duty cycle timer is also stopped when resuming */
3088c2ecf20Sopenharmony_ci	del_timer_sync(&btcoex->no_stomp_timer);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	btcoex->bt_priority_cnt = 0;
3118c2ecf20Sopenharmony_ci	btcoex->bt_priority_time = jiffies;
3128c2ecf20Sopenharmony_ci	clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
3138c2ecf20Sopenharmony_ci	clear_bit(BT_OP_SCAN, &btcoex->op_flags);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	mod_timer(&btcoex->period_timer, jiffies);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/*
3198c2ecf20Sopenharmony_ci * Pause btcoex timer and bt duty cycle timer
3208c2ecf20Sopenharmony_ci */
3218c2ecf20Sopenharmony_civoid ath9k_btcoex_timer_pause(struct ath_softc *sc)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
3248c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE &&
3278c2ecf20Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI)
3288c2ecf20Sopenharmony_ci		return;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n");
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	del_timer_sync(&btcoex->period_timer);
3338c2ecf20Sopenharmony_ci	del_timer_sync(&btcoex->no_stomp_timer);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_civoid ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	del_timer_sync(&btcoex->no_stomp_timer);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ciu16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
3468c2ecf20Sopenharmony_ci	struct ath_mci_profile *mci = &sc->btcoex.mci;
3478c2ecf20Sopenharmony_ci	u16 aggr_limit = 0;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit)
3508c2ecf20Sopenharmony_ci		aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4;
3518c2ecf20Sopenharmony_ci	else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags))
3528c2ecf20Sopenharmony_ci		aggr_limit = min((max_4ms_framelen * 3) / 8,
3538c2ecf20Sopenharmony_ci				 (u32)ATH_AMPDU_LIMIT_MAX);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return aggr_limit;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_civoid ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	if (status & ATH9K_INT_MCI)
3618c2ecf20Sopenharmony_ci		ath_mci_intr(sc);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_civoid ath9k_start_btcoex(struct ath_softc *sc)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (ah->btcoex_hw.enabled ||
3698c2ecf20Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
3738c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
3748c2ecf20Sopenharmony_ci					   AR_STOMP_LOW_WLAN_WGHT, 0);
3758c2ecf20Sopenharmony_ci	else
3768c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_set_weight(ah, 0, 0,
3778c2ecf20Sopenharmony_ci					   ATH_BTCOEX_STOMP_NONE);
3788c2ecf20Sopenharmony_ci	ath9k_hw_btcoex_enable(ah);
3798c2ecf20Sopenharmony_ci	ath9k_btcoex_timer_resume(sc);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_civoid ath9k_stop_btcoex(struct ath_softc *sc)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (!ah->btcoex_hw.enabled ||
3878c2ecf20Sopenharmony_ci	    ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_NONE)
3888c2ecf20Sopenharmony_ci		return;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	ath9k_btcoex_timer_pause(sc);
3918c2ecf20Sopenharmony_ci	ath9k_hw_btcoex_disable(ah);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
3948c2ecf20Sopenharmony_ci		ath_mci_flush_profile(&sc->btcoex.mci);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_civoid ath9k_deinit_btcoex(struct ath_softc *sc)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (ath9k_hw_mci_is_enabled(ah))
4028c2ecf20Sopenharmony_ci		ath_mci_cleanup(sc);
4038c2ecf20Sopenharmony_ci	else {
4048c2ecf20Sopenharmony_ci		enum ath_btcoex_scheme scheme = ath9k_hw_get_btcoex_scheme(ah);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		if (scheme == ATH_BTCOEX_CFG_2WIRE ||
4078c2ecf20Sopenharmony_ci		    scheme == ATH_BTCOEX_CFG_3WIRE)
4088c2ecf20Sopenharmony_ci			ath9k_hw_btcoex_deinit(sc->sc_ah);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ciint ath9k_init_btcoex(struct ath_softc *sc)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct ath_txq *txq;
4158c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
4168c2ecf20Sopenharmony_ci	int r;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	ath9k_hw_btcoex_init_scheme(ah);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	switch (ath9k_hw_get_btcoex_scheme(sc->sc_ah)) {
4218c2ecf20Sopenharmony_ci	case ATH_BTCOEX_CFG_NONE:
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	case ATH_BTCOEX_CFG_2WIRE:
4248c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_init_2wire(sc->sc_ah);
4258c2ecf20Sopenharmony_ci		break;
4268c2ecf20Sopenharmony_ci	case ATH_BTCOEX_CFG_3WIRE:
4278c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_init_3wire(sc->sc_ah);
4288c2ecf20Sopenharmony_ci		ath_init_btcoex_timer(sc);
4298c2ecf20Sopenharmony_ci		txq = sc->tx.txq_map[IEEE80211_AC_BE];
4308c2ecf20Sopenharmony_ci		ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum);
4318c2ecf20Sopenharmony_ci		break;
4328c2ecf20Sopenharmony_ci	case ATH_BTCOEX_CFG_MCI:
4338c2ecf20Sopenharmony_ci		ath_init_btcoex_timer(sc);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
4368c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&sc->btcoex.mci.info);
4378c2ecf20Sopenharmony_ci		ath9k_hw_btcoex_init_mci(ah);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		r = ath_mci_setup(sc);
4408c2ecf20Sopenharmony_ci		if (r)
4418c2ecf20Sopenharmony_ci			return r;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	default:
4458c2ecf20Sopenharmony_ci		WARN_ON(1);
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
4558c2ecf20Sopenharmony_ci	struct ath_mci_profile *mci = &btcoex->mci;
4568c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
4578c2ecf20Sopenharmony_ci	struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
4588c2ecf20Sopenharmony_ci	u32 len = 0;
4598c2ecf20Sopenharmony_ci	int i;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Total BT profiles", NUM_PROF(mci));
4628c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("MGMT", mci->num_mgmt);
4638c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("SCO", mci->num_sco);
4648c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("A2DP", mci->num_a2dp);
4658c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("HID", mci->num_hid);
4668c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("PAN", mci->num_pan);
4678c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("ACL", mci->num_other_acl);
4688c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("BDR", mci->num_bdr);
4698c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Aggr. Limit", mci->aggr_limit);
4708c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type);
4718c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period);
4728c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle);
4738c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time);
4748c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx);
4758c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	len += scnprintf(buf + len, size - len, "BT Weights: ");
4788c2ecf20Sopenharmony_ci	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
4798c2ecf20Sopenharmony_ci		len += scnprintf(buf + len, size - len, "%08x ",
4808c2ecf20Sopenharmony_ci				 btcoex_hw->bt_weight[i]);
4818c2ecf20Sopenharmony_ci	len += scnprintf(buf + len, size - len, "\n");
4828c2ecf20Sopenharmony_ci	len += scnprintf(buf + len, size - len, "WLAN Weights: ");
4838c2ecf20Sopenharmony_ci	for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
4848c2ecf20Sopenharmony_ci		len += scnprintf(buf + len, size - len, "%08x ",
4858c2ecf20Sopenharmony_ci				 btcoex_hw->wlan_weight[i]);
4868c2ecf20Sopenharmony_ci	len += scnprintf(buf + len, size - len, "\n");
4878c2ecf20Sopenharmony_ci	len += scnprintf(buf + len, size - len, "Tx Priorities: ");
4888c2ecf20Sopenharmony_ci	for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++)
4898c2ecf20Sopenharmony_ci		len += scnprintf(buf + len, size - len, "%08x ",
4908c2ecf20Sopenharmony_ci				btcoex_hw->tx_prio[i]);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	len += scnprintf(buf + len, size - len, "\n");
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	return len;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic int ath9k_dump_legacy_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	struct ath_btcoex *btcoex = &sc->btcoex;
5018c2ecf20Sopenharmony_ci	u32 len = 0;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type);
5048c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period);
5058c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle);
5068c2ecf20Sopenharmony_ci	ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	return len;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ciint ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	if (ath9k_hw_mci_is_enabled(sc->sc_ah))
5148c2ecf20Sopenharmony_ci		return ath9k_dump_mci_btcoex(sc, buf, size);
5158c2ecf20Sopenharmony_ci	else
5168c2ecf20Sopenharmony_ci		return ath9k_dump_legacy_btcoex(sc, buf, size);
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
520