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