2011-07-27 12:13:52

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 00/12] ath9k: ASPM fixes

This patch series try to fix ath9k ASPM. Some of patches are cleanup
only or do merging common code (with e1000e driver).

With CONFIG_PCIEASPM it's possible to change ASPM settings on runtime
via /sys/module/pcie_aspm/parameters/policy . However most drivers
I looked at assume this setting is constant. I add a callback to pci
driver to inform about the change, and allow driver to do needed hw
related changes. Currently only ath9k implement the callback, but
I think it will be useful for other drivers (i.e. iwlwifi, rtlwifi)
that do some own ASPM handling.

These patches needs wide testing as they may work on some systems
and not work on others, that can depend on PCIe bridges.

Cc: Jesse Barnes <[email protected]>
Cc: [email protected]
Cc: Jeff Kirsher <[email protected]>
Cc: [email protected]
---
drivers/net/e1000e/netdev.c | 31 ----------
drivers/net/wireless/ath/ath9k/ar9002_hw.c | 12 ---
drivers/net/wireless/ath/ath9k/ar9003_hw.c | 12 ---
drivers/net/wireless/ath/ath9k/hw-ops.h | 9 +-
drivers/net/wireless/ath/ath9k/hw.c | 17 +++--
drivers/net/wireless/ath/ath9k/hw.h | 12 +--
drivers/net/wireless/ath/ath9k/main.c | 8 --
drivers/net/wireless/ath/ath9k/pci.c | 88 ++++++++++++++++++++++-------
drivers/pci/pcie/Makefile | 3
drivers/pci/pcie/aspm.c | 46 ++++++++++++++-
include/linux/pci-aspm.h | 2
include/linux/pci.h | 3
12 files changed, 148 insertions(+), 95 deletions(-)


2011-07-27 12:14:07

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 09/12] e1000e: use common function for disabling ASPM

Cc: Jeff Kirsher <[email protected]>
Cc: [email protected]
Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/e1000e/netdev.c | 31 +------------------------------
1 files changed, 1 insertions(+), 30 deletions(-)

diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 3310c3d..62ee7e4 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -5358,42 +5358,13 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep,
}
}

-#ifdef CONFIG_PCIEASPM
-static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
-{
- pci_disable_link_state_locked(pdev, state);
-}
-#else
-static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
-{
- int pos;
- u16 reg16;
-
- /*
- * Both device and parent should have the same ASPM setting.
- * Disable ASPM in downstream component first and then upstream.
- */
- pos = pci_pcie_cap(pdev);
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~state;
- pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
-
- if (!pdev->bus->self)
- return;
-
- pos = pci_pcie_cap(pdev->bus->self);
- pci_read_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~state;
- pci_write_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, reg16);
-}
-#endif
static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
{
dev_info(&pdev->dev, "Disabling ASPM %s %s\n",
(state & PCIE_LINK_STATE_L0S) ? "L0s" : "",
(state & PCIE_LINK_STATE_L1) ? "L1" : "");

- __e1000e_disable_aspm(pdev, state);
+ pcie_disable_aspm(pdev, state);
}

#ifdef CONFIG_PM
--
1.7.1


2011-07-27 16:12:00

by Jesse Barnes

[permalink] [raw]
Subject: Re: [RFC/RFT v2 00/12] ath9k: ASPM fixes

On Wed, 27 Jul 2011 14:14:47 +0200
Stanislaw Gruszka <[email protected]> wrote:

> This patch series try to fix ath9k ASPM. Some of patches are cleanup
> only or do merging common code (with e1000e driver).
>
> With CONFIG_PCIEASPM it's possible to change ASPM settings on runtime
> via /sys/module/pcie_aspm/parameters/policy . However most drivers
> I looked at assume this setting is constant. I add a callback to pci
> driver to inform about the change, and allow driver to do needed hw
> related changes. Currently only ath9k implement the callback, but
> I think it will be useful for other drivers (i.e. iwlwifi, rtlwifi)
> that do some own ASPM handling.
>
> These patches needs wide testing as they may work on some systems
> and not work on others, that can depend on PCIe bridges.

These look fine, I remember seeing a place where e1000e could use this
just recently and it looks like you took care of it.

We may be able to sneak this in to 3.1 to make things easier on the
dependent drivers, but it'll have to wait until I send my pull request
for the first batch of PCI changes (probably today).

--
Jesse Barnes, Intel Open Source Technology Center

2011-07-27 12:13:52

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 04/12] ath9k: use common PCIe ASPM definces instead of custom ones

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw.h | 4 ----
drivers/net/wireless/ath/ath9k/pci.c | 17 +++++++++++------
2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 2f25577..3f941f0 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -1028,10 +1028,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning);
void ath9k_hw_proc_mib_event(struct ath_hw *ah);
void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan);

-#define ATH_PCIE_CAP_LINK_CTRL 0x70
-#define ATH_PCIE_CAP_LINK_L0S 1
-#define ATH_PCIE_CAP_LINK_L1 2
-
#define ATH9K_CLOCK_RATE_CCK 22
#define ATH9K_CLOCK_RATE_5GHZ_OFDM 40
#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 480e25b..edf7052 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -16,6 +16,7 @@

#include <linux/nl80211.h>
#include <linux/pci.h>
+#include <linux/pci-aspm.h>
#include <linux/ath9k_platform.h>
#include "ath9k.h"

@@ -94,14 +95,16 @@ static void ath_pci_bt_coex_prep(struct ath_common *common)
{
struct ath_softc *sc = (struct ath_softc *) common->priv;
struct pci_dev *pdev = to_pci_dev(sc->dev);
+ int pos;
u8 aspm;

- if (!pci_is_pcie(pdev))
+ pos = pci_pcie_cap(pdev);
+ if (!pos)
return;

- pci_read_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, &aspm);
- aspm &= ~(ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1);
- pci_write_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, aspm);
+ 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);
}

static void ath_pci_extn_synch_enable(struct ath_common *common)
@@ -128,6 +131,7 @@ static void ath_pci_check_aspm(struct ath_softc *sc)
struct ath_hw *ah = sc->sc_ah;
struct pci_dev *pdev = to_pci_dev(sc->dev);
struct pci_dev *parent;
+ int ppos;
u8 aspm;

ah->aspm_enabled = false;
@@ -139,8 +143,9 @@ static void ath_pci_check_aspm(struct ath_softc *sc)
if (WARN_ON(!parent))
return;

- pci_read_config_byte(parent, ATH_PCIE_CAP_LINK_CTRL, &aspm);
- if (aspm & (ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1))
+ ppos = pci_pcie_cap(parent);
+ pci_read_config_byte(parent, ppos + PCI_EXP_LNKCTL, &aspm);
+ if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1))
ah->aspm_enabled = true;
}

--
1.7.1


2011-07-27 12:13:56

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 07/12] pci: aspm: add function for disabling ASPM

Add support for disabling ASPM if !CONFIG_PCIEASPM. Patch is based
on code from e1000e.

Cc: Jesse Barnes <[email protected]>
Cc: [email protected]
Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/pci/pcie/Makefile | 3 +--
drivers/pci/pcie/aspm.c | 41 +++++++++++++++++++++++++++++++++++++++++
include/linux/pci-aspm.h | 2 ++
3 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 00c62df..6e20c59 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -2,8 +2,7 @@
# Makefile for PCI-Express PORT Driver
#

-# Build PCI Express ASPM if needed
-obj-$(CONFIG_PCIEASPM) += aspm.o
+obj-y += aspm.o

pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 6892601..2883fc3 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -21,6 +21,8 @@
#include <linux/pci-aspm.h>
#include "../pci.h"

+#ifdef CONFIG_PCIEASPM
+
#ifdef MODULE_PARAM_PREFIX
#undef MODULE_PARAM_PREFIX
#endif
@@ -976,3 +978,42 @@ bool pcie_aspm_support_enabled(void)
return aspm_support_enabled;
}
EXPORT_SYMBOL(pcie_aspm_support_enabled);
+
+void pcie_disable_aspm(struct pci_dev *pdev, u16 state)
+{
+ pci_disable_link_state_locked(pdev, state);
+}
+EXPORT_SYMBOL(pcie_disable_aspm);
+
+#else /* CONFIG_PCIEASPM */
+
+void pcie_disable_aspm(struct pci_dev *pdev, u16 state)
+{
+ int pos;
+ u16 reg16;
+ struct pci_dev *parent;
+
+ pos = pci_pcie_cap(pdev);
+ if (!pos)
+ return;
+ /*
+ * Both device and parent should have the same ASPM setting.
+ * Disable ASPM in downstream component first and then upstream.
+ */
+
+ pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
+ reg16 &= ~state;
+ pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
+
+ parent = pdev->bus->self;
+ if (WARN_ON(!parent))
+ return;
+
+ pos = pci_pcie_cap(parent);
+ pci_read_config_word(parent, pos + PCI_EXP_LNKCTL, &reg16);
+ reg16 &= ~state;
+ pci_write_config_word(parent, pos + PCI_EXP_LNKCTL, reg16);
+}
+EXPORT_SYMBOL(pcie_disable_aspm);
+
+#endif
diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h
index 7cea7b6..f9722e6 100644
--- a/include/linux/pci-aspm.h
+++ b/include/linux/pci-aspm.h
@@ -55,6 +55,8 @@ static inline void pcie_no_aspm(void)
}
#endif

+extern void pcie_disable_aspm(struct pci_dev *pdev, u16 state);
+
#ifdef CONFIG_PCIEASPM_DEBUG /* this depends on CONFIG_PCIEASPM */
extern void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev);
extern void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev);
--
1.7.1


2011-07-27 12:14:06

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 12/12] ath9k: fix initialization ordering issues

We should initialize registers before ieee80211_register_hw(). For
non RFC posting, this patch will be merged with previous patch
"ath9k: do btcoex ASPM disabling at initialization time"

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw.c | 11 +++++++++++
drivers/net/wireless/ath/ath9k/hw.h | 1 +
drivers/net/wireless/ath/ath9k/pci.c | 23 ++++++++++-------------
3 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 7084bb5..71cfe1a 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -318,6 +318,14 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah)
REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
}

+static void ath9k_hw_aspm_init(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (common->bus_ops->aspm_init)
+ common->bus_ops->aspm_init(common);
+}
+
/* This should work for all families including legacy */
static bool ath9k_hw_chip_test(struct ath_hw *ah)
{
@@ -610,6 +618,9 @@ static int __ath9k_hw_init(struct ath_hw *ah)
if (r)
return r;

+ if (ah->is_pciexpress)
+ ath9k_hw_aspm_init(ah);
+
r = ath9k_hw_init_macaddr(ah);
if (r) {
ath_err(common, "Failed to initialize MAC address\n");
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index eb736ca..ffa907a 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -873,6 +873,7 @@ struct ath_bus_ops {
void (*read_cachesize)(struct ath_common *common, int *csz);
bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
void (*extn_synch_en)(struct ath_common *common);
+ void (*aspm_init)(struct ath_common *common);
};

static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index c46920b..df18f52 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -99,23 +99,15 @@ static void ath_pci_extn_synch_enable(struct ath_common *common)
pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl);
}

-static const struct ath_bus_ops ath_pci_bus_ops = {
- .ath_bus_type = ATH_PCI,
- .read_cachesize = ath_pci_read_cachesize,
- .eeprom_read = ath_pci_eeprom_read,
- .extn_synch_en = ath_pci_extn_synch_enable,
-};
-
-static void ath_pci_aspm_init(struct ath_softc *sc)
+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;

- ah->aspm_enabled = false;
-
if (!pci_is_pcie(pdev))
return;

@@ -138,6 +130,14 @@ static void ath_pci_aspm_init(struct ath_softc *sc)
}
}

+static const struct ath_bus_ops ath_pci_bus_ops = {
+ .ath_bus_type = ATH_PCI,
+ .read_cachesize = ath_pci_read_cachesize,
+ .eeprom_read = ath_pci_eeprom_read,
+ .extn_synch_en = ath_pci_extn_synch_enable,
+ .aspm_init = ath_pci_aspm_init
+};
+
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
void __iomem *mem;
@@ -245,9 +245,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_init;
}

- /* Need to be called after we discover hw capabilities */
- ath_pci_aspm_init(sc);
-
ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name));
wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
hw_name, (unsigned long)mem, pdev->irq);
--
1.7.1


2011-07-27 12:13:53

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 03/12] ath9k: merge common ->config_pci_powersave() checks

Move common checks into wrapper function. Since ASPM can be only enabled
on PCIe devices ->is_pciexpress check is unneeded.

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_hw.c | 3 ---
drivers/net/wireless/ath/ath9k/ar9003_hw.c | 3 ---
drivers/net/wireless/ath/ath9k/hw-ops.h | 3 +++
3 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index 70a18d1..b54ab78 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -308,9 +308,6 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
u8 i;
u32 val;

- if (ah->is_pciexpress != true || ah->aspm_enabled != true)
- return;
-
/* Nothing to do on restore for 11N */
if (!power_off /* !restore */) {
if (AR_SREV_9280_20_OR_LATER(ah)) {
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index e3d58bd..9cf5d13 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -518,9 +518,6 @@ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah)
static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
bool power_off)
{
- if (ah->is_pciexpress != true || ah->aspm_enabled != true)
- return;
-
/* Nothing to do on restore for 11N */
if (!power_off /* !restore */) {
/* set bit 19 to allow forcing of pcie core into L1 state */
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 8c12385..dd9003e 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -24,6 +24,9 @@
static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah,
bool power_off)
{
+ if (ah->aspm_enabled != true)
+ return;
+
ath9k_hw_ops(ah)->config_pci_powersave(ah, power_off);
}

--
1.7.1


2011-07-27 12:14:03

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 10/12] pci: aspm: add settings changed callback

Inform drivers that register pci_drviver->aspm_changed(), about new ASPM
settings. Callback is only called for child (endpoint) devices.

Cc: Jesse Barnes <[email protected]>
Cc: [email protected]
Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/pci/pcie/aspm.c | 5 ++++-
include/linux/pci.h | 3 +++
2 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 2883fc3..d7acec8 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -474,8 +474,11 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
*/
if (state & ASPM_STATE_L1)
pcie_config_aspm_dev(parent, upstream);
- list_for_each_entry(child, &linkbus->devices, bus_list)
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
pcie_config_aspm_dev(child, dwstream);
+ if (child->driver && child->driver->aspm_changed)
+ child->driver->aspm_changed(child, dwstream);
+ }
if (!(state & ASPM_STATE_L1))
pcie_config_aspm_dev(parent, upstream);

diff --git a/include/linux/pci.h b/include/linux/pci.h
index c446b5c..2110d63 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -555,6 +555,9 @@ struct pci_driver {
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
+#ifdef CONFIG_PCIEASPM
+ void (*aspm_changed) (struct pci_dev *dev, u32 state);
+#endif
struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
--
1.7.1


2011-07-27 12:13:53

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 02/12] ath9k: remove ->config_pci_powersave() redundant argument

We always call ->config_pci_powersave() with both restore and power_off
arguments equal to 0 or both equal to 1, so merge them into one
argument.

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_hw.c | 5 ++---
drivers/net/wireless/ath/ath9k/ar9003_hw.c | 5 ++---
drivers/net/wireless/ath/ath9k/hw-ops.h | 5 ++---
drivers/net/wireless/ath/ath9k/hw.c | 2 +-
drivers/net/wireless/ath/ath9k/hw.h | 3 +--
drivers/net/wireless/ath/ath9k/main.c | 6 +++---
6 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index 44d9d8d..70a18d1 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -303,8 +303,7 @@ static void ar9002_hw_init_mode_gain_regs(struct ath_hw *ah)
* register as the other analog registers. Hence the 9 writes.
*/
static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
- int restore,
- int power_off)
+ bool power_off)
{
u8 i;
u32 val;
@@ -313,7 +312,7 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
return;

/* Nothing to do on restore for 11N */
- if (!restore) {
+ if (!power_off /* !restore */) {
if (AR_SREV_9280_20_OR_LATER(ah)) {
/*
* AR9280 2.0 or later chips use SerDes values from the
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index ad2bb2b..e3d58bd 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -516,14 +516,13 @@ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah)
* register as the other analog registers. Hence the 9 writes.
*/
static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
- int restore,
- int power_off)
+ bool power_off)
{
if (ah->is_pciexpress != true || ah->aspm_enabled != true)
return;

/* Nothing to do on restore for 11N */
- if (!restore) {
+ if (!power_off /* !restore */) {
/* set bit 19 to allow forcing of pcie core into L1 state */
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);

diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index cb29e88..8c12385 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -22,10 +22,9 @@
/* Hardware core and driver accessible callbacks */

static inline void ath9k_hw_configpcipowersave(struct ath_hw *ah,
- int restore,
- int power_off)
+ bool power_off)
{
- ath9k_hw_ops(ah)->config_pci_powersave(ah, restore, power_off);
+ ath9k_hw_ops(ah)->config_pci_powersave(ah, power_off);
}

static inline void ath9k_hw_rxena(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 6bda08e..4ef7313 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -597,7 +597,7 @@ static int __ath9k_hw_init(struct ath_hw *ah)


if (ah->is_pciexpress)
- ath9k_hw_configpcipowersave(ah, 0, 0);
+ ath9k_hw_configpcipowersave(ah, false);
else
ath9k_hw_disablepcie(ah);

diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 7dd78e7..2f25577 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -601,8 +601,7 @@ struct ath_hw_private_ops {
*/
struct ath_hw_ops {
void (*config_pci_powersave)(struct ath_hw *ah,
- int restore,
- int power_off);
+ bool power_off);
void (*rx_enable)(struct ath_hw *ah);
void (*set_desc_link)(void *ds, u32 link);
bool (*calibrate)(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 9098aaa..651f633 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -887,7 +887,7 @@ static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);

- ath9k_hw_configpcipowersave(ah, 0, 0);
+ ath9k_hw_configpcipowersave(ah, false);

if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah);
@@ -967,7 +967,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)

ath9k_hw_phy_disable(ah);

- ath9k_hw_configpcipowersave(ah, 1, 1);
+ ath9k_hw_configpcipowersave(ah, true);

spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
@@ -1066,7 +1066,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
init_channel = ath9k_cmn_get_curchannel(hw, ah);

/* Reset SERDES registers */
- ath9k_hw_configpcipowersave(ah, 0, 0);
+ ath9k_hw_configpcipowersave(ah, false);

/*
* The basic interface to setting the hardware in a good
--
1.7.1


2011-07-27 12:13:55

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 06/12] ath9k: init PCIe PM and SERDES registers if ASPM is enabled

During init, we currently skip calling ->config_pci_powersave() because
ah->aspm_enabled is not initialized. Fix that and initialize registers
after we discover if ASPM is enabled.

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw.c | 5 +----
drivers/net/wireless/ath/ath9k/pci.c | 5 ++++-
2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 4ef7313..7084bb5 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -595,10 +595,7 @@ static int __ath9k_hw_init(struct ath_hw *ah)

ath9k_hw_init_mode_regs(ah);

-
- if (ah->is_pciexpress)
- ath9k_hw_configpcipowersave(ah, false);
- else
+ if (!ah->is_pciexpress)
ath9k_hw_disablepcie(ah);

if (!AR_SREV_9300_20_OR_LATER(ah))
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 16ff992..494ec0f 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -148,8 +148,11 @@ static void ath_pci_aspm_init(struct ath_softc *sc)

ppos = pci_pcie_cap(parent);
pci_read_config_byte(parent, ppos + PCI_EXP_LNKCTL, &aspm);
- if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1))
+ 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);
+ }
}

static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
--
1.7.1


2011-07-27 12:13:58

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 08/12] ath9k: use common function for disabling ASPM

Disable ASPM using common function, this works also with
CONFIG_PCIEASPM=y.

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/pci.c | 29 ++++++-----------------------
1 files changed, 6 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 494ec0f..a92e55f 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -111,43 +111,26 @@ static void ath_pci_aspm_init(struct ath_softc *sc)
struct ath_hw *ah = sc->sc_ah;
struct pci_dev *pdev = to_pci_dev(sc->dev);
struct pci_dev *parent;
- int pos, ppos;
+ int pos;
u8 aspm;

ah->aspm_enabled = false;

- pos = pci_pcie_cap(pdev);
- if (!pos)
+ if (!pci_is_pcie(pdev))
return;

- parent = pdev->bus->self;
-
if (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) {
/* Bluetooth coexistance requires disabling ASPM. */
- 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.
- */
- if (WARN_ON(!parent))
- return;
-
- ppos = pci_pcie_cap(parent);
- pci_read_config_byte(parent, ppos + PCI_EXP_LNKCTL, &aspm);
- aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
- pci_write_config_byte(parent, ppos + PCI_EXP_LNKCTL, aspm);
-
+ pcie_disable_aspm(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
return;
}

+ parent = pdev->bus->self;
if (WARN_ON(!parent))
return;

- ppos = pci_pcie_cap(parent);
- pci_read_config_byte(parent, ppos + PCI_EXP_LNKCTL, &aspm);
+ 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. */
--
1.7.1


2011-07-27 12:14:03

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 11/12] ath9k: be prepare for dynamic ASPM settings change

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 <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw-ops.h | 1 +
drivers/net/wireless/ath/ath9k/hw.h | 1 +
drivers/net/wireless/ath/ath9k/pci.c | 31 +++++++++++++++++++++++++++++++
3 files changed, 33 insertions(+), 0 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 6abf26d..eb736ca 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/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index a92e55f..c46920b 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -286,6 +286,34 @@ 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)
+ ath9k_hw_configpcipowersave(ah, true);
+ ah->aspm_enabled = false;
+ }
+
+ spin_unlock_irq(&sc->sc_pcu_lock);
+}
+
+#endif
+
#ifdef CONFIG_PM

static int ath_pci_suspend(struct device *device)
@@ -365,6 +393,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


2011-07-27 12:13:52

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 01/12] ath9k: skip ->config_pci_powersave() if PCIe port has ASPM disabled

We receive many bug reports about system hang during suspend/resume
when ath9k driver is in use. Adrian Chadd remarked that this problem
happens on systems that have ASPM disabled.

To do not hit the bug, skip doing ->config_pci_powersave magic if PCIe
downstream port device, which ath9k device is connected to, has ASPM
disabled.

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_hw.c | 6 +-----
drivers/net/wireless/ath/ath9k/ar9003_hw.c | 6 +-----
drivers/net/wireless/ath/ath9k/hw.c | 1 -
drivers/net/wireless/ath/ath9k/hw.h | 2 +-
drivers/net/wireless/ath/ath9k/pci.c | 23 +++++++++++++++++++++++
5 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index 9ff7c30..44d9d8d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -309,11 +309,7 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
u8 i;
u32 val;

- if (ah->is_pciexpress != true)
- return;
-
- /* Do not touch SerDes registers */
- if (ah->config.pcie_powersave_enable == 2)
+ if (ah->is_pciexpress != true || ah->aspm_enabled != true)
return;

/* Nothing to do on restore for 11N */
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index 8efdec2..ad2bb2b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -519,11 +519,7 @@ static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
int restore,
int power_off)
{
- if (ah->is_pciexpress != true)
- return;
-
- /* Do not touch SerDes registers */
- if (ah->config.pcie_powersave_enable == 2)
+ if (ah->is_pciexpress != true || ah->aspm_enabled != true)
return;

/* Nothing to do on restore for 11N */
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 8006ce0..6bda08e 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -378,7 +378,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.additional_swba_backoff = 0;
ah->config.ack_6mb = 0x0;
ah->config.cwm_ignore_extcca = 0;
- ah->config.pcie_powersave_enable = 0;
ah->config.pcie_clock_req = 0;
ah->config.pcie_waen = 0;
ah->config.analog_shiftreg = 1;
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 6acd0f9..7dd78e7 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -219,7 +219,6 @@ struct ath9k_ops_config {
int additional_swba_backoff;
int ack_6mb;
u32 cwm_ignore_extcca;
- u8 pcie_powersave_enable;
bool pcieSerDesWrite;
u8 pcie_clock_req;
u32 pcie_waen;
@@ -673,6 +672,7 @@ struct ath_hw {

bool sw_mgmt_crypto;
bool is_pciexpress;
+ bool aspm_enabled;
bool is_monitoring;
bool need_an_top2_fixup;
u16 tx_trig_level;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 3bad0b2..480e25b 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -123,6 +123,27 @@ static const struct ath_bus_ops ath_pci_bus_ops = {
.extn_synch_en = ath_pci_extn_synch_enable,
};

+static void ath_pci_check_aspm(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct pci_dev *pdev = to_pci_dev(sc->dev);
+ struct pci_dev *parent;
+ u8 aspm;
+
+ ah->aspm_enabled = false;
+
+ if (!pci_is_pcie(pdev))
+ return;
+
+ parent = pdev->bus->self;
+ if (WARN_ON(!parent))
+ return;
+
+ pci_read_config_byte(parent, ATH_PCIE_CAP_LINK_CTRL, &aspm);
+ if (aspm & (ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1))
+ ah->aspm_enabled = true;
+}
+
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
void __iomem *mem;
@@ -230,6 +251,8 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_init;
}

+ ath_pci_check_aspm(sc);
+
ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name));
wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
hw_name, (unsigned long)mem, pdev->irq);
--
1.7.1


2011-07-29 13:18:56

by Stanislaw Gruszka

[permalink] [raw]
Subject: Re: [RFC/RFT v2 00/12] ath9k: ASPM fixes

On Wed, Jul 27, 2011 at 09:11:50AM -0700, Jesse Barnes wrote:
> On Wed, 27 Jul 2011 14:14:47 +0200
> Stanislaw Gruszka <[email protected]> wrote:
>
> > This patch series try to fix ath9k ASPM. Some of patches are cleanup
> > only or do merging common code (with e1000e driver).
> >
> > With CONFIG_PCIEASPM it's possible to change ASPM settings on runtime
> > via /sys/module/pcie_aspm/parameters/policy . However most drivers
> > I looked at assume this setting is constant. I add a callback to pci
> > driver to inform about the change, and allow driver to do needed hw
> > related changes. Currently only ath9k implement the callback, but
> > I think it will be useful for other drivers (i.e. iwlwifi, rtlwifi)
> > that do some own ASPM handling.
> >
> > These patches needs wide testing as they may work on some systems
> > and not work on others, that can depend on PCIe bridges.
>
> These look fine, I remember seeing a place where e1000e could use this
> just recently and it looks like you took care of it.

Yep, accually some pci changes I did was based on e1000e code.

> We may be able to sneak this in to 3.1 to make things easier on the
> dependent drivers, but it'll have to wait until I send my pull request
> for the first batch of PCI changes (probably today).

This could go 3.2, except first patch (ath9k specific) which directly
address system lockups. I'm going to a bit rework and post 1st patch
today. Rest patches from the series I'll repost next week.

Thanks
Stanislaw

2011-07-27 12:13:53

by Stanislaw Gruszka

[permalink] [raw]
Subject: [RFC/RFT v2 05/12] ath9k: do btcoex ASPM disabling at initialization time

Disable ASPM in pci .probe, to do not change settings when
pci_bus_sem is not held.

Disable ASPM on upstream (device) and downstream (PCIe port) component.
According to e1000e driver authors this is required. I did not find that
requirement in PCIe spec, but it seems to be logical for me.

This need to be fixed for CONFIG_PCIEASPM, that will be done later ...

Signed-off-by: Stanislaw Gruszka <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw.h | 1 -
drivers/net/wireless/ath/ath9k/main.c | 2 -
drivers/net/wireless/ath/ath9k/pci.c | 52 +++++++++++++++++---------------
3 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 3f941f0..6abf26d 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -871,7 +871,6 @@ struct ath_bus_ops {
enum ath_bus_type ath_bus_type;
void (*read_cachesize)(struct ath_common *common, int *csz);
bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
- void (*bt_coex_prep)(struct ath_common *common);
void (*extn_synch_en)(struct ath_common *common);
};

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 651f633..b0389c3 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1141,8 +1141,6 @@ static int ath9k_start(struct ieee80211_hw *hw)
AR_STOMP_LOW_WLAN_WGHT);
ath9k_hw_btcoex_enable(ah);

- if (common->bus_ops->bt_coex_prep)
- common->bus_ops->bt_coex_prep(common);
if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
ath9k_btcoex_timer_resume(sc);
}
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index edf7052..16ff992 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -88,25 +88,6 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data)
return true;
}

-/*
- * Bluetooth coexistance requires disabling ASPM.
- */
-static void ath_pci_bt_coex_prep(struct ath_common *common)
-{
- struct ath_softc *sc = (struct ath_softc *) common->priv;
- struct pci_dev *pdev = to_pci_dev(sc->dev);
- int pos;
- u8 aspm;
-
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return;
-
- 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);
-}
-
static void ath_pci_extn_synch_enable(struct ath_common *common)
{
struct ath_softc *sc = (struct ath_softc *) common->priv;
@@ -122,24 +103,46 @@ static const struct ath_bus_ops ath_pci_bus_ops = {
.ath_bus_type = ATH_PCI,
.read_cachesize = ath_pci_read_cachesize,
.eeprom_read = ath_pci_eeprom_read,
- .bt_coex_prep = ath_pci_bt_coex_prep,
.extn_synch_en = ath_pci_extn_synch_enable,
};

-static void ath_pci_check_aspm(struct ath_softc *sc)
+static void ath_pci_aspm_init(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct pci_dev *pdev = to_pci_dev(sc->dev);
struct pci_dev *parent;
- int ppos;
+ int pos, ppos;
u8 aspm;

ah->aspm_enabled = false;

- if (!pci_is_pcie(pdev))
+ pos = pci_pcie_cap(pdev);
+ if (!pos)
return;

parent = pdev->bus->self;
+
+ if (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) {
+ /* Bluetooth coexistance requires disabling ASPM. */
+ 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.
+ */
+ if (WARN_ON(!parent))
+ return;
+
+ ppos = pci_pcie_cap(parent);
+ pci_read_config_byte(parent, ppos + PCI_EXP_LNKCTL, &aspm);
+ aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+ pci_write_config_byte(parent, ppos + PCI_EXP_LNKCTL, aspm);
+
+ return;
+ }
+
if (WARN_ON(!parent))
return;

@@ -256,7 +259,8 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_init;
}

- ath_pci_check_aspm(sc);
+ /* Need to be called after we discover hw capabilities */
+ ath_pci_aspm_init(sc);

ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name));
wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
--
1.7.1