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