static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; if (sc->sbands[IEEE80211_BAND_2GHZ].channels) kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); if (sc->sbands[IEEE80211_BAND_5GHZ].channels) kfree(sc->sbands[IEEE80211_BAND_5GHZ].channels); if ((sc->btcoex.no_stomp_timer) && ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE) ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); if (ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_MCI) ath_mci_cleanup(sc); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); ath9k_hw_deinit(sc->sc_ah); kfree(sc->sc_ah); sc->sc_ah = NULL; }
/* * Pause btcoex timer and bt duty cycle timer */ void ath9k_btcoex_timer_pause(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_hw *ah = sc->sc_ah; if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE && ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI) return; ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n"); del_timer_sync(&btcoex->period_timer); del_timer_sync(&btcoex->no_stomp_timer); }
static int ath9k_init_btcoex(struct ath_softc *sc) { struct ath_txq *txq; struct ath_hw *ah = sc->sc_ah; int r; switch (ath9k_hw_get_btcoex_scheme(sc->sc_ah)) { case ATH_BTCOEX_CFG_NONE: break; case ATH_BTCOEX_CFG_2WIRE: ath9k_hw_btcoex_init_2wire(sc->sc_ah); break; case ATH_BTCOEX_CFG_3WIRE: ath9k_hw_btcoex_init_3wire(sc->sc_ah); r = ath_init_btcoex_timer(sc); if (r) return -1; txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; break; case ATH_BTCOEX_CFG_MCI: sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; INIT_LIST_HEAD(&sc->btcoex.mci.info); r = ath_mci_setup(sc); if (r) return r; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) { ah->btcoex_hw.mci.ready = false; ah->btcoex_hw.mci.bt_state = 0; ah->btcoex_hw.mci.bt_ver_major = 3; ah->btcoex_hw.mci.bt_ver_minor = 0; ah->btcoex_hw.mci.bt_version_known = false; ah->btcoex_hw.mci.update_2g5g = true; ah->btcoex_hw.mci.is_2g = true; ah->btcoex_hw.mci.wlan_channels_update = false; ah->btcoex_hw.mci.wlan_channels[0] = 0x00000000; ah->btcoex_hw.mci.wlan_channels[1] = 0xffffffff; ah->btcoex_hw.mci.wlan_channels[2] = 0xffffffff; ah->btcoex_hw.mci.wlan_channels[3] = 0x7fffffff; ah->btcoex_hw.mci.query_bt = true; ah->btcoex_hw.mci.unhalt_bt_gpm = true; ah->btcoex_hw.mci.halted_bt_gpm = false; ah->btcoex_hw.mci.need_flush_btinfo = false; ah->btcoex_hw.mci.wlan_cal_seq = 0; ah->btcoex_hw.mci.wlan_cal_done = 0; ah->btcoex_hw.mci.config = 0x2201; } break; default: WARN_ON(1); break; } return 0; }
/* Need to be called after we discover btcoex capabilities */ static void ath_pci_aspm_init(struct ath_common *common) { struct ath_softc *sc = (struct ath_softc *) common->priv; struct ath_hw *ah = sc->sc_ah; struct pci_dev *pdev = to_pci_dev(sc->dev); struct pci_dev *parent; u16 aspm; if (!ah->is_pciexpress) return; parent = pdev->bus->self; if (!parent) return; if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && (AR_SREV_9285(ah))) { /* Bluetooth coexistence requires disabling ASPM. */ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1); /* * Both upstream and downstream PCIe components should * have the same ASPM settings. */ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1); ath_info(common, "Disabling ASPM since BTCOEX is enabled\n"); return; } /* * 0x70c - Ack Frequency Register. * * Bits 27:29 - DEFAULT_L1_ENTRANCE_LATENCY. * * 000 : 1 us * 001 : 2 us * 010 : 4 us * 011 : 8 us * 100 : 16 us * 101 : 32 us * 110/111 : 64 us */ if (AR_SREV_9462(ah)) pci_read_config_dword(pdev, 0x70c, &ah->config.aspm_l1_fix); pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm); if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) { ah->aspm_enabled = true; /* Initialize PCIe PM and SERDES registers. */ ath9k_hw_configpcipowersave(ah, false); ath_info(common, "ASPM enabled: 0x%x\n", aspm); } }
void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; if (ah->btcoex_hw.enabled && ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath_htc_cancel_btcoex_work(priv); ath9k_hw_btcoex_disable(ah); } }
void ath9k_htc_start_btcoex(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) { ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_LOW_WLAN_WGHT, 0); ath9k_hw_btcoex_enable(ah); ath_htc_resume_btcoex_work(priv); } }
/* * (Re)start btcoex timers */ void ath9k_btcoex_timer_resume(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_hw *ah = sc->sc_ah; if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_3WIRE && ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI) return; ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ del_timer_sync(&btcoex->no_stomp_timer); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); clear_bit(BT_OP_SCAN, &btcoex->op_flags); mod_timer(&btcoex->period_timer, jiffies); }
/* Need to be called after we discover btcoex capabilities */ static void ath_pci_aspm_init(struct ath_common *common) { struct ath_softc *sc = (struct ath_softc *) common->priv; struct ath_hw *ah = sc->sc_ah; struct pci_dev *pdev = to_pci_dev(sc->dev); struct pci_dev *parent; int pos; u8 aspm; if (!ah->is_pciexpress) return; pos = pci_pcie_cap(pdev); if (!pos) return; parent = pdev->bus->self; if (!parent) return; if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && (AR_SREV_9285(ah))) { /* Bluetooth coexistance requires disabling ASPM for AR9285. */ pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm); aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm); /* * Both upstream and downstream PCIe components should * have the same ASPM settings. */ pos = pci_pcie_cap(parent); pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm); aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm); ath_info(common, "Disabling ASPM since BTCOEX is enabled\n"); return; } pos = pci_pcie_cap(parent); pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm); if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) { ah->aspm_enabled = true; /* Initialize PCIe PM and SERDES registers. */ ath9k_hw_configpcipowersave(ah, false); ath_info(common, "ASPM enabled: 0x%x\n", aspm); } }
void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int qnum; /* * Check if BTCOEX is globally disabled. */ if (!common->btcoex_enabled) { ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_NONE; return; } if (product && strncmp(product, ATH_HTC_BTCOEX_PRODUCT_ID, 5) == 0) { ah->btcoex_hw.scheme = ATH_BTCOEX_CFG_3WIRE; } switch (ath9k_hw_get_btcoex_scheme(priv->ah)) { case ATH_BTCOEX_CFG_NONE: break; case ATH_BTCOEX_CFG_3WIRE: priv->ah->btcoex_hw.btactive_gpio = 7; priv->ah->btcoex_hw.btpriority_gpio = 6; priv->ah->btcoex_hw.wlanactive_gpio = 8; priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; ath9k_hw_btcoex_init_3wire(priv->ah); ath_htc_init_btcoex_work(priv); qnum = priv->hwq_map[IEEE80211_AC_BE]; ath9k_hw_init_btcoex_hw(priv->ah, qnum); break; default: WARN_ON(1); break; } }