Kernel offer possibility to change ASPM settings using /sys/module/pcie_aspm/parameters/policy interface. As settings can be changed, we also need to change ath9k hw registers. Patch implement this by monitoring power_off state, and based on that state setup register when pci core inform us about ASPM change. Signed-off-by: Stanislaw Gruszka <sgruszka@xxxxxxxxxx> --- drivers/net/wireless/ath/ath9k/hw-ops.h | 1 + drivers/net/wireless/ath/ath9k/hw.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 3 +- drivers/net/wireless/ath/ath9k/pci.c | 38 +++++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index dd9003e..0743e82 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -24,6 +24,7 @@ static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { + ah->power_off = power_off; if (ah->aspm_enabled != true) return; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index f28f339..cb388ff 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -672,6 +672,7 @@ struct ath_hw { bool sw_mgmt_crypto; bool is_pciexpress; bool aspm_enabled; + bool power_off; bool is_monitoring; bool need_an_top2_fixup; u16 tx_trig_level; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b0389c3..a7e3c5c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1065,6 +1065,8 @@ static int ath9k_start(struct ieee80211_hw *hw) init_channel = ath9k_cmn_get_curchannel(hw, ah); + spin_lock_bh(&sc->sc_pcu_lock); + /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -1075,7 +1077,6 @@ static int ath9k_start(struct ieee80211_hw *hw) * be followed by initialization of the appropriate bits * and then setup of the interrupt mask. */ - spin_lock_bh(&sc->sc_pcu_lock); r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (r) { ath_err(common, diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 67a2c96..c585180 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -285,6 +285,41 @@ static void ath_pci_remove(struct pci_dev *pdev) pci_release_region(pdev, 0); } +#ifdef CONFIG_PCIEASPM + +static void ath_pci_aspm_changed(struct pci_dev *pdev, u32 state) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + + spin_lock_irq(&sc->sc_pcu_lock); + + /* + * ASPM settings were changed, we have to configure PCIe PM and SERDES + * registers based on current state of device and new ASPM settings. + */ + if (state & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) { + ah->aspm_enabled = true; + ath9k_hw_configpcipowersave(ah, ah->power_off); + } else { + if (ah->power_off == false) { + /* + * Device is enabled, but since we disable ASPM we also + * setup PCIe PM registers in off state. + */ + ath9k_hw_configpcipowersave(ah, true); + /* But track real power_off value. */ + ah->power_off = false; + } + ah->aspm_enabled = false; + } + + spin_unlock_irq(&sc->sc_pcu_lock); +} + +#endif + #ifdef CONFIG_PM static int ath_pci_suspend(struct device *device) @@ -364,6 +399,9 @@ static struct pci_driver ath_pci_driver = { .id_table = ath_pci_id_table, .probe = ath_pci_probe, .remove = ath_pci_remove, +#ifdef CONFIG_PCIEASPM + .aspm_changed = ath_pci_aspm_changed, +#endif .driver.pm = ATH9K_PM_OPS, }; -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html