@@ -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;
@@ -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;
@@ -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,
@@ -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,
};
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@redhat.com> --- 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(-)