* [RFC 4/5] mac80211: add a struct for info upon device stop
2009-05-11 9:24 [RFC 0/5] mac80211 suspend cleanups/helpers Luis R. Rodriguez
` (2 preceding siblings ...)
2009-05-11 9:25 ` [RFC 3/5] mac80211: fix idle trigger upon resume Luis R. Rodriguez
@ 2009-05-11 9:25 ` Luis R. Rodriguez
2009-05-11 9:53 ` Johannes Berg
2009-05-11 9:25 ` [RFC 5/5] ath9k: Add Wake-on-Wireless-LAN support Luis R. Rodriguez
4 siblings, 1 reply; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-05-11 9:25 UTC (permalink / raw)
To: johannes, linux-wireless; +Cc: Luis R. Rodriguez
This allows us to stuff information to give to the driver
for stoping the device. This will come more in handy later
when we add WoW support.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/adm8211.c | 3 +-
drivers/net/wireless/at76c50x-usb.c | 3 +-
drivers/net/wireless/ath/ar9170/main.c | 3 +-
drivers/net/wireless/ath/ath5k/base.c | 6 +++-
drivers/net/wireless/ath/ath9k/main.c | 3 +-
drivers/net/wireless/b43/main.c | 3 +-
drivers/net/wireless/b43legacy/main.c | 3 +-
drivers/net/wireless/iwlwifi/iwl-agn.c | 3 +-
drivers/net/wireless/iwlwifi/iwl3945-base.c | 3 +-
drivers/net/wireless/libertas_tf/main.c | 3 +-
drivers/net/wireless/mac80211_hwsim.c | 3 +-
drivers/net/wireless/mwl8k.c | 3 +-
drivers/net/wireless/p54/p54common.c | 3 +-
drivers/net/wireless/rt2x00/rt2x00.h | 3 +-
drivers/net/wireless/rt2x00/rt2x00mac.c | 3 +-
drivers/net/wireless/rtl818x/rtl8180_dev.c | 3 +-
drivers/net/wireless/rtl818x/rtl8187_dev.c | 3 +-
drivers/net/wireless/wl12xx/main.c | 3 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 3 +-
include/net/mac80211.h | 39 +++++++++++++++++++++++---
net/mac80211/driver-ops.h | 5 ++-
net/mac80211/iface.c | 18 +++++++++---
net/mac80211/pm.c | 4 ++-
23 files changed, 93 insertions(+), 33 deletions(-)
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index 316df03..2efe158 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1546,7 +1546,8 @@ fail:
return retval;
}
-static void adm8211_stop(struct ieee80211_hw *dev)
+static void adm8211_stop(struct ieee80211_hw *dev,
+ struct ieee80211_device_stop_info *stop_info)
{
struct adm8211_priv *priv = dev->priv;
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index e3caeef..54d71c6 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1766,7 +1766,8 @@ error:
return 0;
}
-static void at76_mac80211_stop(struct ieee80211_hw *hw)
+static void at76_mac80211_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct at76_priv *priv = hw->priv;
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 4ef1d2f..f87546d 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1003,7 +1003,8 @@ out:
return err;
}
-static void ar9170_op_stop(struct ieee80211_hw *hw)
+static void ar9170_op_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct ar9170 *ar = hw->priv;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 6789c5d..ef2563c 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -221,7 +221,8 @@ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
static int ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel);
static int ath5k_reset_wake(struct ath5k_softc *sc);
static int ath5k_start(struct ieee80211_hw *hw);
-static void ath5k_stop(struct ieee80211_hw *hw);
+static void ath5k_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info);
static int ath5k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
static void ath5k_remove_interface(struct ieee80211_hw *hw,
@@ -2696,7 +2697,8 @@ static int ath5k_start(struct ieee80211_hw *hw)
return ath5k_init(hw->priv);
}
-static void ath5k_stop(struct ieee80211_hw *hw)
+static void ath5k_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
ath5k_stop_hw(hw->priv);
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4fe0f8a..23a3a4c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2118,7 +2118,8 @@ exit:
return 0;
}
-static void ath9k_stop(struct ieee80211_hw *hw)
+static void ath9k_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 2615aaf..ffe0bf0 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4348,7 +4348,8 @@ static int b43_op_start(struct ieee80211_hw *hw)
return err;
}
-static void b43_op_stop(struct ieee80211_hw *hw)
+static void b43_op_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev;
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 07c7898..3ac73c5 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -3514,7 +3514,8 @@ out_mutex_unlock:
return err;
}
-static void b43legacy_op_stop(struct ieee80211_hw *hw)
+static void b43legacy_op_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
struct b43legacy_wldev *dev = wl->current_dev;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 6cdee0b..4355812 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2001,7 +2001,8 @@ out:
return 0;
}
-static void iwl_mac_stop(struct ieee80211_hw *hw)
+static void iwl_mac_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct iwl_priv *priv = hw->priv;
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index f6c1489..ad002e6 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3411,7 +3411,8 @@ out_release_irq:
return ret;
}
-static void iwl3945_mac_stop(struct ieee80211_hw *hw)
+static void iwl3945_mac_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct iwl_priv *priv = hw->priv;
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index 10a99e2..d3db5ca 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -291,7 +291,8 @@ err_prog_firmware:
return ret;
}
-static void lbtf_op_stop(struct ieee80211_hw *hw)
+static void lbtf_op_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct lbtf_private *priv = hw->priv;
unsigned long flags;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index b1213b6..2c191fa 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -477,7 +477,8 @@ static int mac80211_hwsim_start(struct ieee80211_hw *hw)
}
-static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
+static void mac80211_hwsim_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct mac80211_hwsim_data *data = hw->priv;
data->started = 0;
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 46b288d..303b952 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -2916,7 +2916,8 @@ static int mwl8k_stop_wt(struct work_struct *wt)
return rc;
}
-static void mwl8k_stop(struct ieee80211_hw *hw)
+static void mwl8k_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
int rc;
struct mwl8k_stop_worker *worker;
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
index 48d81d9..67d4483 100644
--- a/drivers/net/wireless/p54/p54common.c
+++ b/drivers/net/wireless/p54/p54common.c
@@ -2087,7 +2087,8 @@ out:
return err;
}
-static void p54_stop(struct ieee80211_hw *dev)
+static void p54_stop(struct ieee80211_hw *dev,
+ struct ieee80211_device_stop_info *stop_info)
{
struct p54_common *priv = dev->priv;
struct sk_buff *skb;
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 419b1b9..db7d61f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -958,7 +958,8 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
*/
int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
int rt2x00mac_start(struct ieee80211_hw *hw);
-void rt2x00mac_stop(struct ieee80211_hw *hw);
+void rt2x00mac_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info);
int rt2x00mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index c4c06b4..e9f4c3a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -178,7 +178,8 @@ int rt2x00mac_start(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(rt2x00mac_start);
-void rt2x00mac_stop(struct ieee80211_hw *hw)
+void rt2x00mac_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 7e65d7c..d84a597 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -627,7 +627,8 @@ static int rtl8180_start(struct ieee80211_hw *dev)
return ret;
}
-static void rtl8180_stop(struct ieee80211_hw *dev)
+static void rtl8180_stop(struct ieee80211_hw *dev,
+ struct ieee80211_device_stop_info *stop_info)
{
struct rtl8180_priv *priv = dev->priv;
u8 reg;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 158827e..62a462c 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -991,7 +991,8 @@ static int rtl8187_start(struct ieee80211_hw *dev)
return 0;
}
-static void rtl8187_stop(struct ieee80211_hw *dev)
+static void rtl8187_stop(struct ieee80211_hw *dev,
+ struct ieee80211_device_stop_info *stop_info)
{
struct rtl8187_priv *priv = dev->priv;
struct sk_buff *skb;
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 603d611..971794b 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -363,7 +363,8 @@ out:
return ret;
}
-static void wl12xx_op_stop(struct ieee80211_hw *hw)
+static void wl12xx_op_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct wl12xx *wl = hw->priv;
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6bdb170..db3d894 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -256,7 +256,8 @@ out:
return r;
}
-static void zd_op_stop(struct ieee80211_hw *hw)
+static void zd_op_stop(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info)
{
struct zd_mac *mac = zd_hw_mac(hw);
struct zd_chip *chip = &mac->chip;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 38dc1cd..d7460a4 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1291,6 +1291,31 @@ enum ieee80211_ampdu_mlme_action {
};
/**
+ * enum ieee80211_device_stop_reason - device stop reasons
+ *
+ * These are reasons why mac80211 would call the driver's stop()
+ * callback. Devices should probably not care other than when
+ * we call to stop during suspend, in these cases the device may
+ * want to leave the radio on for WoW events for example.
+ * @IEEE80211_DEV_STOP_INVALID_MAC: invalid mac address was detected for device
+ * @IEEE80211_DEV_STOP_NO_OPEN_DEV: we were not able to open any
+ netdevice for the current wireless device during user initialization.
+ * @IEEE80211_DEV_STOP_DEV_CLOSE_REQUEST: user requested the device to be
+ * closed.
+ * @IEEE80211_DEV_STOP_SUSPEND: we are going to suspend
+ */
+enum ieee80211_device_stop_reasons {
+ IEEE80211_DEV_STOP_INVALID_MAC,
+ IEEE80211_DEV_STOP_NO_OPEN_DEV,
+ IEEE80211_DEV_STOP_DEV_CLOSE_REQUEST,
+ IEEE80211_DEV_STOP_SUSPEND,
+};
+
+struct ieee80211_device_stop_info {
+ enum ieee80211_device_stop_reasons reason;
+};
+
+/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
* This structure contains various callbacks that the driver may
@@ -1318,10 +1343,13 @@ enum ieee80211_ampdu_mlme_action {
* Must be implemented.
*
* @stop: Called after last netdevice attached to the hardware
- * is disabled. This should turn off the hardware (at least
- * it must turn off frame reception.)
- * May be called right after add_interface if that rejects
- * an interface.
+ * is disabled or during suspend. This should turn off the
+ * hardware (at least it must turn off frame reception) unless
+ * the device wants to enable Wake-on-Wireless-LAN.
+ * This may be called right after add_interface if that rejects
+ * an interface. To assist drivers we provide stop detail
+ * information through &struct ieee80211_device_stop_info. This
+ * will help later for WoW support.
* Must be implemented.
*
* @add_interface: Called when a netdevice attached to the hardware is
@@ -1443,7 +1471,8 @@ enum ieee80211_ampdu_mlme_action {
struct ieee80211_ops {
int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
int (*start)(struct ieee80211_hw *hw);
- void (*stop)(struct ieee80211_hw *hw);
+ void (*stop)(struct ieee80211_hw *hw,
+ struct ieee80211_device_stop_info *stop_info);
int (*add_interface)(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
void (*remove_interface)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 3912b53..3864353 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -14,9 +14,10 @@ static inline int drv_start(struct ieee80211_local *local)
return local->ops->start(&local->hw);
}
-static inline void drv_stop(struct ieee80211_local *local)
+static inline void drv_stop(struct ieee80211_local *local,
+ struct ieee80211_device_stop_info *stop_info)
{
- local->ops->stop(&local->hw);
+ local->ops->stop(&local->hw, stop_info);
}
static inline int drv_add_interface(struct ieee80211_local *local,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index f8eefad..54d9b91 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -81,6 +81,7 @@ static int ieee80211_open(struct net_device *dev)
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct ieee80211_if_init_conf conf;
+ struct ieee80211_device_stop_info stop_info;
u32 changed = 0;
int res;
u32 hw_reconf_flags = 0;
@@ -198,8 +199,10 @@ static int ieee80211_open(struct net_device *dev)
* Validate the MAC address for this device.
*/
if (!is_valid_ether_addr(dev->dev_addr)) {
- if (!local->open_count)
- drv_stop(local);
+ if (!local->open_count) {
+ stop_info.reason = IEEE80211_DEV_STOP_INVALID_MAC;
+ drv_stop(local, &stop_info);
+ }
return -EADDRNOTAVAIL;
}
@@ -331,8 +334,10 @@ static int ieee80211_open(struct net_device *dev)
err_del_interface:
drv_remove_interface(local, &conf);
err_stop:
- if (!local->open_count)
- drv_stop(local);
+ if (!local->open_count) {
+ stop_info.reason = IEEE80211_DEV_STOP_NO_OPEN_DEV;
+ drv_stop(local, &stop_info);
+ }
err_del_bss:
sdata->bss = NULL;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -555,10 +560,13 @@ static int ieee80211_stop(struct net_device *dev)
ieee80211_recalc_ps(local, -1);
if (local->open_count == 0) {
+ struct ieee80211_device_stop_info stop_info;
+
if (netif_running(local->mdev))
dev_close(local->mdev);
- drv_stop(local);
+ stop_info.reason = IEEE80211_DEV_STOP_DEV_CLOSE_REQUEST;
+ drv_stop(local, &stop_info);
ieee80211_led_radio(local, 0);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9d3d89a..24aa109 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -67,8 +67,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
/* stop hardware */
if (local->open_count) {
+ struct ieee80211_device_stop_info stop_info;
ieee80211_led_radio(local, false);
- drv_stop(local);
+ stop_info.reason = IEEE80211_DEV_STOP_SUSPEND;
+ drv_stop(local, &stop_info);
}
return 0;
}
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread* [RFC 5/5] ath9k: Add Wake-on-Wireless-LAN support
2009-05-11 9:24 [RFC 0/5] mac80211 suspend cleanups/helpers Luis R. Rodriguez
` (3 preceding siblings ...)
2009-05-11 9:25 ` [RFC 4/5] mac80211: add a struct for info upon device stop Luis R. Rodriguez
@ 2009-05-11 9:25 ` Luis R. Rodriguez
4 siblings, 0 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-05-11 9:25 UTC (permalink / raw)
To: johannes, linux-wireless; +Cc: Luis R. Rodriguez
This adds WoWLAN suppport for ath9k. We support this initially
through debugfs. Once this gets proper testing we can move it
through proper driver APIs. We start by enabling only magic
packet WoWLAN.
You can use regular ethernet Wake-on-LAN applications to trigger
a WoWLAN event.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/ath/ath9k/Makefile | 1 +
drivers/net/wireless/ath/ath9k/ath9k.h | 19 +
drivers/net/wireless/ath/ath9k/debug.c | 51 +++
drivers/net/wireless/ath/ath9k/debug.h | 4 +
drivers/net/wireless/ath/ath9k/hw.c | 22 ++
drivers/net/wireless/ath/ath9k/hw.h | 21 ++
drivers/net/wireless/ath/ath9k/initvals.h | 31 ++
drivers/net/wireless/ath/ath9k/main.c | 21 ++
drivers/net/wireless/ath/ath9k/pci.c | 87 +++++
drivers/net/wireless/ath/ath9k/reg.h | 154 ++++++++
drivers/net/wireless/ath/ath9k/wow.c | 543 +++++++++++++++++++++++++++++
11 files changed, 954 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath9k/wow.c
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 783bc39..d4a598d 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -14,5 +14,6 @@ ath9k-y += hw.o \
ath9k-$(CONFIG_PCI) += pci.o
ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
+ath9k-$(CONFIG_PM) += wow.o
obj-$(CONFIG_ATH9K) += ath9k.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 79a167c..b386b27 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -599,12 +599,31 @@ struct ath_softc {
struct ath_rfkill rf_kill;
struct ath_ani ani;
struct ath9k_node_stats nodestats;
+#ifdef CONFIG_PM
+ bool wow_enable;
+ bool wow_asleep;
+ bool wow_got_bmiss_intr;
+ bool wow_sleep_proc_intr;
+ u32 wow_intr_before_sleep;
+#endif
#ifdef CONFIG_ATH9K_DEBUG
struct ath9k_debug debug;
#endif
struct ath_bus_ops *bus_ops;
};
+#ifdef CONFIG_PM
+/*
+ * WoW trigger types
+ */
+#define AH_WOW_USER_PATTERN_EN 0x1
+#define AH_WOW_MAGIC_PATTERN_EN 0x2
+#define AH_WOW_LINK_CHANGE 0x4
+#define AH_WOW_BEACON_MISS 0x8
+#define AH_WOW_MAX_EVENTS 4
+
+#endif
+
struct ath_wiphy {
struct ath_softc *sc; /* shared for all virtual wiphys */
struct ieee80211_hw *hw;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 97df20c..86d7d56 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -493,6 +493,45 @@ static const struct file_operations fops_wiphy = {
.owner = THIS_MODULE
};
+#ifdef CONFIG_PM
+static ssize_t read_file_wow_enable(struct file *file,
+ char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ char buf[3];
+ int res;
+
+ res = scnprintf(buf, 3, "%c\n", sc->wow_enable ? '1' : '0');
+ return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+
+static ssize_t write_file_wow_enable(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ char buf[2];
+ size_t len;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ if (buf[0] == '1')
+ sc->wow_enable = true;
+ else
+ sc->wow_enable = false;
+ return count;
+}
+
+static const struct file_operations fops_wow_enable = {
+ .read = read_file_wow_enable,
+ .write = write_file_wow_enable,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE
+};
+#endif
int ath9k_init_debug(struct ath_softc *sc)
{
@@ -525,6 +564,15 @@ int ath9k_init_debug(struct ath_softc *sc)
if (!sc->debug.debugfs_rcstat)
goto err;
+#ifdef CONFIG_PM
+ sc->debug.debugfs_wow_enable = debugfs_create_file("wow_enable",
+ S_IRUGO | S_IWUSR,
+ sc->debug.debugfs_phy,
+ sc, &fops_wow_enable);
+ if (!sc->debug.debugfs_wow_enable)
+ goto err;
+#endif
+
sc->debug.debugfs_wiphy = debugfs_create_file(
"wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc,
&fops_wiphy);
@@ -540,6 +588,9 @@ err:
void ath9k_exit_debug(struct ath_softc *sc)
{
debugfs_remove(sc->debug.debugfs_wiphy);
+#ifdef CONFIG_PM
+ debugfs_remove(sc->debug.debugfs_wow_enable);
+#endif
debugfs_remove(sc->debug.debugfs_rcstat);
debugfs_remove(sc->debug.debugfs_interrupt);
debugfs_remove(sc->debug.debugfs_dma);
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 23298b9..c488591 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -29,6 +29,7 @@ enum ATH_DEBUG {
ATH_DBG_BEACON = 0x00000100,
ATH_DBG_CONFIG = 0x00000200,
ATH_DBG_FATAL = 0x00000400,
+ ATH_DBG_POWER_MGT = 0x00000800,
ATH_DBG_ANY = 0xffffffff
};
@@ -103,6 +104,9 @@ struct ath9k_debug {
struct dentry *debugfs_interrupt;
struct dentry *debugfs_rcstat;
struct dentry *debugfs_wiphy;
+#ifdef CONFIG_PM
+ struct dentry *debugfs_wow_enable;
+#endif
struct ath_stats stats;
};
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 5879c73..73114de 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -718,6 +718,12 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285_1_2),
2);
}
+#ifdef CONFIG_PM
+ /* SerDes values during WOW sleep */
+ INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
+ ar9285PciePhy_AWOW_9285_1_2,
+ ARRAY_SIZE(ar9285PciePhy_AWOW_9285_1_2), 2);
+#endif
} else if (AR_SREV_9285_10_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285,
ARRAY_SIZE(ar9285Modes_9285), 6);
@@ -748,6 +754,12 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
ar9280PciePhy_clkreq_always_on_L1_9280,
ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2);
}
+#ifdef CONFIG_PM
+ /* SerDes values during WOW sleep */
+ INIT_INI_ARRAY(&ah->iniPcieSerdesWow, ar9280PciePhy_AWOW_9280,
+ ARRAY_SIZE(ar9280PciePhy_AWOW_9280), 2);
+#endif
+
INIT_INI_ARRAY(&ah->iniModesAdditional,
ar9280Modes_fast_clock_9280_2,
ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), 3);
@@ -893,6 +905,16 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
goto bad;
}
+#ifdef CONFIG_PM
+ /* WOW */
+ ath9k_wow_set_gpio_reset_low(ah);
+ /* Clear the Wow Status */
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, REG_READ(ah, AR_PCIE_PM_CTRL) |
+ AR_PMCTRL_WOW_PME_CLR);
+ REG_WRITE(ah, AR_WOW_PATTERN_REG,
+ AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN_REG)));
+#endif
+
if (AR_SREV_9285(ah))
ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S);
else
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index ddb24c4..ff602f6 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -521,6 +521,11 @@ struct ath_hw {
int initPDADC;
int PDADCdelta;
+#ifdef CONFIG_PM
+ /* WoW mask -- used to indicate which WoW we have enabled */
+ u32 ah_wowEventMask;
+#endif
+
struct ar5416IniArray iniModes;
struct ar5416IniArray iniCommon;
struct ar5416IniArray iniBank0;
@@ -536,6 +541,10 @@ struct ath_hw {
struct ar5416IniArray iniModesAdditional;
struct ar5416IniArray iniModesRxGain;
struct ar5416IniArray iniModesTxGain;
+#ifdef CONFIG_PM
+ /* SerDes values during WOW sleep */
+ struct ar5416IniArray iniPcieSerdesWow;
+#endif
};
/* Attach, Detach, Reset */
@@ -617,4 +626,16 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
void ath9k_hw_btcoex_enable(struct ath_hw *ah);
+#ifdef CONFIG_PM
+
+/* WOW - Wake on Wireless */
+void ath9k_wow_set_gpio_reset_low(struct ath_hw *ah);
+/* Called when going to suspend/hibernate */
+int ath9k_hw_wow_enable(struct ath_hw *ah, u32 patternEnable);
+/* Called when coming back up from suspend/hibernation */
+u32 ath9k_hw_wow_wake_up(struct ath_hw *ah);
+const char *ath9k_hw_wow_event_to_string(u32 wow_event);
+
+#endif /* CONFIG_PM */
+
#endif
diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h
index e2f0a34..12c3e7b 100644
--- a/drivers/net/wireless/ath/ath9k/initvals.h
+++ b/drivers/net/wireless/ath/ath9k/initvals.h
@@ -3439,6 +3439,22 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
{0x00004044, 0x00000000 },
};
+#ifdef CONFIG_PM
+/* Auto generated PCI-E PHY config for Merlin with WOW */
+static const u_int32_t ar9280PciePhy_AWOW_9280[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01ddffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+#endif
+
/* AR9285 */
static const u_int32_t ar9285Modes_9285[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
@@ -4846,3 +4862,18 @@ static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = {
{0x00004040, 0x00043007 },
{0x00004044, 0x00000000 },
};
+
+#ifdef CONFIG_PM
+static const u_int32_t ar9285PciePhy_AWOW_9285_1_2[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01ddffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+#endif /* CONFIG_PM */
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 23a3a4c..4940fe8 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -560,6 +560,18 @@ irqreturn_t ath_isr(int irq, void *dev)
ath9k_hw_set_interrupts(ah, sc->imask);
}
+ if (status & ATH9K_INT_BMISS) {
+#ifdef CONFIG_PM
+ if (sc->wow_sleep_proc_intr) {
+ DPRINTF(sc, (ATH_DBG_POWER_MGT | ATH_DBG_INTERRUPT),
+ "during WoW we got a BMISS\n");
+ sc->wow_got_bmiss_intr = true;
+ sc->wow_sleep_proc_intr = false;
+ }
+#endif
+ DPRINTF(sc, ATH_DBG_INTERRUPT,
+ "spurious unattended beacon miss interrupt\n");
+ }
if (status & ATH9K_INT_TIM_TIMER) {
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
/* Clear RxAbort bit so that we can
@@ -2126,6 +2138,15 @@ static void ath9k_stop(struct ieee80211_hw *hw,
aphy->state = ATH_WIPHY_INACTIVE;
+#ifdef CONFIG_PM
+ if (sc->wow_enable &&
+ stop_info->reason == IEEE80211_DEV_STOP_SUSPEND) {
+ DPRINTF(sc, ATH_DBG_ANY, "Leaving radio on during "
+ "suspend/hibernate for WoW\n");
+ return;
+ }
+#endif
+
if (sc->sc_flags & SC_OP_INVALID) {
DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
return;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 168411d..3bf6d6e 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -133,6 +133,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+
ret = pci_request_region(pdev, 0, "ath9k");
if (ret) {
dev_err(&pdev->dev, "PCI memory region reserve error\n");
@@ -219,12 +222,56 @@ static void ath_pci_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM
+static void ath9k_pci_wow_enable(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ int r;
+ u32 wake_up_events;
+
+ /* We'll add more support as we go */
+ wake_up_events = AH_WOW_MAGIC_PATTERN_EN |
+ AH_WOW_LINK_CHANGE |
+ AH_WOW_BEACON_MISS;
+
+ /* eventually we'll add this...
+ * if (wake_up_events & AH_WOW_USER_PATTERN_EN)
+ * ath9k_wow_create_pattern(sc);
+ */
+
+ /*
+ * To avoid false wake, we enable beacon miss interrupt only when
+ * we go to sleep. We save the current interrupt mask so that
+ * we can restore it after the system wakes up.
+ */
+ sc->wow_intr_before_sleep = ah->mask_reg;
+ ath9k_hw_set_interrupts(ah, ATH9K_INT_BMISS | ATH9K_INT_GLOBAL);
+ sc->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
+
+ r = ath9k_hw_wow_enable(ah, wake_up_events);
+ if (r) {
+ DPRINTF(sc, ATH_DBG_ANY,
+ "Unable to enable WoW\n");
+ return;
+ }
+
+ device_set_wakeup_enable(sc->dev, 1);
+
+ DPRINTF(sc, ATH_DBG_ANY,
+ "WoW enabled\n");
+
+ sc->wow_sleep_proc_intr = true;
+ sc->wow_asleep = true;
+}
+
static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ if (sc->wow_enable && device_can_wakeup(&pdev->dev))
+ ath9k_pci_wow_enable(sc);
+
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
@@ -234,11 +281,48 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
pci_save_state(pdev);
pci_disable_device(pdev);
+ if (sc->wow_enable && device_can_wakeup(&pdev->dev)) {
+ pci_prepare_to_sleep(pdev);
+ return 0;
+ }
+ pci_wake_from_d3(pdev, sc->wow_enable);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
+static void ath9k_pci_wow_wake(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ u32 wow_status;
+
+ sc->wow_asleep = false;
+ ath9k_hw_set_interrupts(ah, sc->wow_intr_before_sleep);
+ sc->imask = sc->wow_intr_before_sleep;
+
+ wow_status = ath9k_hw_wow_wake_up(sc->sc_ah);
+
+ if (wow_status)
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "Waking up due to WoW signal %s\n",
+ ath9k_hw_wow_event_to_string(wow_status));
+
+ if (sc->wow_got_bmiss_intr) {
+ /*
+ * Some devices may not pick beacon miss
+ * as the reason they woke up so we add that
+ * here for that shortcoming
+ */
+ wow_status |= AH_WOW_BEACON_MISS;
+ sc->wow_got_bmiss_intr = false;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "WoW status: %d\n WoW reason: %s\n",
+ wow_status,
+ ath9k_hw_wow_event_to_string((wow_status & 0x0FFF)));
+}
+
static int ath_pci_resume(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
@@ -251,6 +335,9 @@ static int ath_pci_resume(struct pci_dev *pdev)
return err;
pci_restore_state(pdev);
+ if (sc->wow_enable)
+ ath9k_pci_wow_wake(sc);
+
/* Enable LED */
ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 5260524..387bbe7 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -665,6 +665,11 @@
#define AR_RC_HOSTIF 0x00000100
#define AR_WA 0x4004
+#define AR_WA_UNTIE_RESET_EN (1 << 15) /* Enable PCI Reset to POR (power-on-reset) */
+#define AR_WA_RESET_EN (1 << 18) /* Sw Control to enable PCI-Reset to POR (bit 15) */
+#define AR_WA_ANALOG_SHIFT (1 << 20)
+#define AR_WA_POR_SHORT (1 << 21) /* PCI-E Phy reset control */
+
#define AR9285_WA_DEFAULT 0x004a05cb
#define AR9280_WA_DEFAULT 0x0040073f
#define AR_WA_DEFAULT 0x0000073f
@@ -1508,4 +1513,153 @@ enum {
#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24)
#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28)
+/* WoW - Wake On Wireless */
+
+#define AR_PMCTRL_AUX_PWR_DET 0x10000000 /* Puts Chip in L2 state */
+#define AR_PMCTRL_D3COLD_VAUX 0x00800000
+#define AR_PMCTRL_HOST_PME_EN 0x00400000 /* Send OOB WAKE_L on WoW event */
+#define AR_PMCTRL_WOW_PME_CLR 0x00200000 /* Clear WoW event */
+#define AR_PMCTRL_PWR_STATE_MASK 0x0F000000 /* Power State Mask */
+#define AR_PMCTRL_PWR_STATE_D1D3 0x0F000000 /* Activate D1 and D3 */
+#define AR_PMCTRL_PWR_STATE_D0 0x08000000 /* Activate D0 */
+#define AR_PMCTRL_PWR_PM_CTRL_ENA 0x00008000 /* Enable power management */
+
+#define AR_WOW_BEACON_TIMO_MAX 0xFFFFFFFF /* Max. value for Beacon Timeout */
+
+/*
+ * MAC WoW Registers.
+ */
+#define AR_WOW_PATTERN_REG 0x825C
+#define AR_WOW_COUNT_REG 0x8260
+#define AR_WOW_BCN_EN_REG 0x8270
+#define AR_WOW_BCN_TIMO_REG 0x8274
+#define AR_WOW_KEEP_ALIVE_TIMO_REG 0x8278
+#define AR_WOW_KEEP_ALIVE_REG 0x827C
+#define AR_WOW_US_SCALAR_REG 0x8284
+#define AR_WOW_KEEP_ALIVE_DELAY_REG 0x8288
+#define AR_WOW_PATTERN_MATCH_REG 0x828C
+#define AR_WOW_PATTERN_OFF1_REG 0x8290 /* Pattern bytes 0 -> 3 */
+#define AR_WOW_PATTERN_OFF2_REG 0x8294 /* Pattern bytes 4 -> 7 */
+/* For AR9285 or Later version of chips */
+#define AR_WOW_EXACT_REG 0x829C
+#define AR_WOW_LENGTH1_REG 0x8360
+#define AR_WOW_LENGTH2_REG 0x8364
+/* Register to enable pattern match for less than 256 bytes packets */
+#define AR_WOW_PATTERN_MATCH_LT_256B_REG 0x8368
+
+/* AR_WOW_PATTERN_REG Values */
+#define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 27) /* in usecs */
+#define AR_WOW_MAC_INTR_EN 0x00040000
+#define AR_WOW_MAGIC_EN 0x00010000
+#define AR_WOW_PATTERN_EN(x) ((x & 0xff) << 0)
+#define AR_WOW_PATTERN_FOUND_SHIFT 8
+#define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PATTERN_FOUND_SHIFT))
+#define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PATTERN_FOUND_SHIFT)
+#define AR_WOW_MAGIC_PAT_FOUND 0x00020000
+#define AR_WOW_MAC_INTR 0x00080000
+#define AR_WOW_KEEP_ALIVE_FAIL 0x00100000
+#define AR_WOW_BEACON_FAIL 0x00200000
+
+#define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \
+ AR_WOW_MAGIC_PAT_FOUND | \
+ AR_WOW_KEEP_ALIVE_FAIL | \
+ AR_WOW_BEACON_FAIL))
+#define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \
+ AR_WOW_MAGIC_EN | \
+ AR_WOW_MAC_INTR_EN | \
+ AR_WOW_BEACON_FAIL | \
+ AR_WOW_KEEP_ALIVE_FAIL))
+
+/* AR_WOW_COUNT_REG Values */
+#define AR_WOW_AIFS_CNT(x) ((x & 0xff) << 0)
+#define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8)
+#define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16)
+
+/* AR_WOW_BCN_EN_REG */
+#define AR_WOW_BEACON_FAIL_EN 0x00000001
+
+/* AR_WOW_BCN_TIMO_REG */
+#define AR_WOW_BEACON_TIMO 0x40000000 /* Valid if BCN_EN is set */
+
+/* AR_WOW_KEEP_ALIVE_TIMO_REG */
+#define AR_WOW_KEEP_ALIVE_TIMO 0x00007A12
+#define AR_WOW_KEEP_ALIVE_NEVER 0xFFFFFFFF
+
+/* AR_WOW_KEEP_ALIVE_REG */
+#define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001
+#define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002
+
+/* AR_WOW_KEEP_ALIVE_DELAY_REG */
+#define AR_WOW_KEEP_ALIVE_DELAY 0x000003E8 /* 1 msec */
+
+/*
+ * Keep it long for Beacon workaround - ensures no false alarm
+ */
+#define AR_WOW_BMISSTHRESHOLD 0x20
+
+/* AR_WOW_PATTERN_MATCH_REG */
+#define AR_WOW_PAT_END_OF_PKT(x) ((x & 0xf) << 0)
+#define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8)
+
+/*
+ * Default values for Wow Configuration for backoff, aifs, slot, keep-alive, etc
+ * to be programmed into various registers.
+ */
+#define AR_WOW_PAT_BACKOFF 0x00000004 /* AR_WOW_PATTERN_REG */
+#define AR_WOW_CNT_AIFS_CNT 0x00000022 /* AR_WOW_COUNT_REG */
+#define AR_WOW_CNT_SLOT_CNT 0x00000009 /* AR_WOW_COUNT_REG */
+/*
+ * Keepalive count applicable for AR9280 2.0 and above.
+ */
+#define AR_WOW_CNT_KA_CNT 0x00000008 /* AR_WOW_COUNT_REG */
+
+/* WoW - Transmit buffer for keep alive frames */
+#define AR_WOW_TRANSMIT_BUFFER 0xE000 /* E000 - EFFC */
+
+#define AR_WOW_KA_DESC_WORD2 0xE000
+#define AR_WOW_KA_DESC_WORD3 0xE004
+#define AR_WOW_KA_DESC_WORD4 0xE008
+#define AR_WOW_KA_DESC_WORD5 0xE00C
+#define AR_WOW_KA_DESC_WORD6 0xE010
+#define AR_WOW_KA_DESC_WORD7 0xE014
+#define AR_WOW_KA_DESC_WORD8 0xE018
+#define AR_WOW_KA_DESC_WORD9 0xE01C
+#define AR_WOW_KA_DESC_WORD10 0xE020
+#define AR_WOW_KA_DESC_WORD11 0xE024
+#define AR_WOW_KA_DESC_WORD12 0xE028
+#define AR_WOW_KA_DESC_WORD13 0xE02C
+
+#define AR_WOW_KA_DATA_WORD0 0xE030
+#define AR_WOW_KA_DATA_WORD1 0xE034
+#define AR_WOW_KA_DATA_WORD2 0xE038
+#define AR_WOW_KA_DATA_WORD3 0xE03C
+#define AR_WOW_KA_DATA_WORD4 0xE040
+#define AR_WOW_KA_DATA_WORD5 0xE044
+
+/* WoW Transmit Buffer for patterns */
+#define AR_WOW_TB_PATTERN0 0xE100
+#define AR_WOW_TB_PATTERN1 0xE200
+#define AR_WOW_TB_PATTERN2 0xE300
+#define AR_WOW_TB_PATTERN3 0xE400
+#define AR_WOW_TB_PATTERN4 0xE500
+#define AR_WOW_TB_PATTERN5 0xE600
+#define AR_WOW_TB_PATTERN6 0xE700
+#define AR_WOW_TB_PATTERN7 0xE800
+#define AR_WOW_TB_MASK0 0xEC00
+#define AR_WOW_TB_MASK1 0xEC20
+#define AR_WOW_TB_MASK2 0xEC40
+#define AR_WOW_TB_MASK3 0xEC60
+#define AR_WOW_TB_MASK4 0xEC80
+#define AR_WOW_TB_MASK5 0xECa0
+#define AR_WOW_TB_MASK6 0xECC0
+#define AR_WOW_TB_MASK7 0xECE0
+
+/* Currently Pattern 0-7 are supported - so bit 0-7 are set */
+#define AR_WOW_PATTERN_SUPPORTED 0xFF
+#define AR_WOW_LENGTH_MAX 0xFF
+#define AR_WOW_LENGTH1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3)
+#define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LENGTH1_SHIFT(_i))
+#define AR_WOW_LENGTH2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3)
+#define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LENGTH2_SHIFT(_i))
+
#endif
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
new file mode 100644
index 0000000..11fee62
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+#include "reg.h"
+
+#ifdef CONFIG_PM
+
+/*
+ * This routine is called to configure the SerDes register for the
+ * AR9280 2.0 and above chip during WOW sleep.
+ */
+static void
+ath9k_ar928xConfigSerDes_WowSleep(struct ath_hw *ah)
+{
+ unsigned int i;
+
+ /*
+ * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ
+ * are both enabled. This uses more power but in certain cases this
+ * is required as otherwise WOW sleep is unstable and chip may
+ * disappears.
+ */
+ for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
+ REG_WRITE(ah,
+ INI_RA(&ah->iniPcieSerdesWow, i, 0),
+ INI_RA(&ah->iniPcieSerdesWow, i, 1));
+ udelay(1000);
+}
+
+static bool ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
+{
+ u32 frame_len = 28;
+ u32 tpc = 0x3f;
+ u32 antenna_mode = 1;
+ u32 transmit_rate;
+ u32 frame_type = 0x2; /* Frame Type -> Data */
+ u32 sub_type = 0x4; /* Subtype -> Null Data */
+ u32 to_ds = 1;
+ u32 duration_id = 0x3d;
+ u8 *StaMacAddr, *ApMacAddr;
+ u8 *addr1, *addr2, *addr3;
+ u32 ctl[12] = { 0 };
+ u32 data_word0 = 0, data_word1 = 0, data_word2 = 0,
+ data_word3 = 0, data_word4 = 0, data_word5 = 0;
+ u32 i;
+
+ StaMacAddr = (u8 *)ah->macaddr;
+ ApMacAddr = (u8 *)ah->ah_sc->curbssid;
+ addr2 = StaMacAddr;
+ addr1 = addr3 = ApMacAddr;
+
+ /*
+ * XXX: we need a way to determine if the AP we're on
+ * is using CCK only and if so use this:
+ * transmit_rate = 0x1B; // CCK_1M
+ * For now we just assume your AP supports OFDM
+ */
+ transmit_rate = 0xB; /* OFDM_6M */
+
+ /* Set the Transmit Buffer. */
+ ctl[0] = (frame_len | (tpc << 16)) + (antenna_mode << 25);
+ ctl[1] = 0;
+ ctl[2] = 0x7 << 16; /* tx_tries0 */
+ ctl[3] = transmit_rate;
+ ctl[4] = 0;
+ ctl[7] = ah->txchainmask << 2;
+
+ for (i = 0; i < 12; i++)
+ REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
+
+ data_word0 = (frame_type << 2) | (sub_type << 4) |
+ (to_ds << 8) | (duration_id << 16);
+ data_word1 = (((u32)addr1[3] << 24) | ((u32)addr1[2] << 16) |
+ ((u32)addr1[1]) << 8 | ((u32)addr1[0]));
+ data_word2 = (((u32)addr2[1] << 24) | ((u32)addr2[0] << 16) |
+ ((u32)addr1[5]) << 8 | ((u32)addr1[4]));
+ data_word3 = (((u32)addr2[5] << 24) | ((u32)addr2[4] << 16) |
+ ((u32)addr2[3]) << 8 | ((u32)addr2[2]));
+ data_word4 = (((u32)addr3[3] << 24) | ((u32)addr3[2] << 16) |
+ ((u32)addr3[1]) << 8 | (u32)addr3[0]);
+ data_word5 = (((u32)addr3[5]) << 8 | ((u32)addr3[4]));
+
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD0, data_word0);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD1, data_word1);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD2, data_word2);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD3, data_word3);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD4, data_word4);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD5, data_word5);
+
+ return true;
+}
+
+/* TBD: Should querying hw for hardware capability */
+#define MAX_PATTERN_SIZE 256
+#define MAX_PATTERN_MASK_SIZE 32
+
+/* Deducting the disassociate/deauthenticate packets */
+#define MAX_NUM_USER_PATTERN 6
+
+# if 0
+static void ath9k_wow_apply_pattern(struct ath_hw *ah,
+ u8 *pAthPattern, u8 *pAthMask,
+ int pattern_count, u32 athPatternLen)
+{
+ unsigned int i;
+ u32 reg_pat[] = {
+ AR_WOW_TB_PATTERN0,
+ AR_WOW_TB_PATTERN1,
+ AR_WOW_TB_PATTERN2,
+ AR_WOW_TB_PATTERN3,
+ AR_WOW_TB_PATTERN4,
+ AR_WOW_TB_PATTERN5,
+ AR_WOW_TB_PATTERN6,
+ AR_WOW_TB_PATTERN7
+ };
+ u32 reg_mask[] = {
+ AR_WOW_TB_MASK0,
+ AR_WOW_TB_MASK1,
+ AR_WOW_TB_MASK2,
+ AR_WOW_TB_MASK3,
+ AR_WOW_TB_MASK4,
+ AR_WOW_TB_MASK5,
+ AR_WOW_TB_MASK6,
+ AR_WOW_TB_MASK7
+ };
+ u32 pattern_val;
+ u32 mask_val;
+ u8 mask_bit = 0x1;
+ u8 pattern;
+
+ /* TBD: should check count by querying the hardware capability */
+ if (pattern_count >= MAX_NUM_USER_PATTERN)
+ return;
+
+ pattern = (u8)REG_READ(ah, AR_WOW_PATTERN_REG);
+ pattern = pattern | (mask_bit << pattern_count);
+ REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern);
+
+ /* Set the registers for pattern */
+ for (i = 0; i < MAX_PATTERN_SIZE; i+=4) {
+ pattern_val = (((u32)pAthPattern[i]) |
+ ((u32)pAthPattern[i+1] << 8) |
+ ((u32)pAthPattern[i+2] << 16) |
+ ((u32)pAthPattern[i+3] << 24));
+ REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val);
+ }
+
+ /* Set the registers for mask */
+ for (i = 0; i < MAX_PATTERN_MASK_SIZE; i+=4) {
+ mask_val = (((u32)pAthMask[i]) |
+ ((u32)pAthMask[i+1] << 8) |
+ ((u32)pAthMask[i+2] << 16) |
+ ((u32)pAthMask[i+3] << 24));
+ REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val);
+ }
+
+ if (AR_SREV_9285_10_OR_LATER(ah)) {
+ /* Set the pattern length to be matched */
+ u32 val;
+ if (pattern_count < 4) {
+ /* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */
+ val = REG_READ(ah, AR_WOW_LENGTH1_REG);
+ val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) |
+ ((athPatternLen & AR_WOW_LENGTH_MAX)
+ << AR_WOW_LENGTH1_SHIFT(pattern_count)));
+ REG_WRITE(ah, AR_WOW_LENGTH1_REG, val);
+ } else {
+ /* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */
+ val = REG_READ(ah, AR_WOW_LENGTH2_REG);
+ val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) |
+ ((athPatternLen & AR_WOW_LENGTH_MAX)
+ << AR_WOW_LENGTH2_SHIFT(pattern_count)));
+ REG_WRITE(ah, AR_WOW_LENGTH2_REG, val);
+ }
+ }
+
+ ah->ah_wowEventMask |=
+ (1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT));
+}
+#endif
+
+static bool ath9k_set_power_mode_wow_sleep(struct ath_hw *ah)
+{
+ REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
+
+ REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Set receive disable bit */
+ if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGT,
+ "dma failed to stop in 10ms\n"
+ "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n",
+ REG_READ(ah, AR_CR),
+ REG_READ(ah, AR_DIAG_SW));
+ return false;
+ } else {
+ REG_WRITE(ah, AR_RXDP, 0x0);
+
+ /* AR9280 2.0/2.1 WOW has sleep issue, do not set it to sleep */
+ if (AR_SREV_9280_20(ah))
+ return true;
+ else
+ REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_ON_INT);
+ return true;
+ }
+}
+
+int ath9k_hw_wow_enable(struct ath_hw *ah, u32 patternEnable)
+{
+ u32 init_val, val, rval = 0;
+ /* Send a Keep-Alive frame every 900 millisec */
+ const int ka_timo = 900;
+ /* Delay of 4 millisec between two KeepAlive's */
+ const int ka_delay = 4;
+ u32 wow_event_mask;
+ bool all_triggers_set = true;
+
+ /*
+ * ah_wowEventMask is a mask to the AR_WOW_PATTERN_REG register to
+ * indicate which WOW events that we have enabled. The WOW Events
+ * are from the patternEnable in this function and pattern_count of
+ * ath9k_wow_apply_pattern()
+ */
+ wow_event_mask = ah->ah_wowEventMask;
+
+ /*
+ * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep,
+ * we do not want the Reset from the PCI-E to disturb our hw state.
+ */
+ if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress) {
+ /*
+ * We need to untie the internal POR (power-on-reset) to the
+ * external PCI-E reset. We also need to tie the PCI-E Phy
+ * reset to the PCI-E reset.
+ */
+ u32 wa_reg_val;
+ if (AR_SREV_9285(ah))
+ wa_reg_val = AR9285_WA_DEFAULT;
+ else
+ wa_reg_val = AR9280_WA_DEFAULT;
+ wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN);
+ wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT;
+ REG_WRITE(ah, AR_WA, wa_reg_val);
+
+ if (!AR_SREV_9285(ah) || AR_SREV_9285_12_OR_LATER(ah)) {
+ /*
+ * For WOW sleep, we reprogram the SerDes so that the
+ * PLL and CHK REQ are both enabled. This uses more
+ * power but otherwise in certain cases, WOW sleep is
+ * unusable and chip may disappears.
+ */
+ ath9k_ar928xConfigSerDes_WowSleep(ah);
+ }
+ }
+
+ /*
+ * Set the power states appropriately and enable pme.
+ */
+ val = REG_READ(ah, AR_PCIE_PM_CTRL);
+ val |= AR_PMCTRL_HOST_PME_EN |
+ AR_PMCTRL_PWR_PM_CTRL_ENA |
+ AR_PMCTRL_AUX_PWR_DET;
+ val &= ~AR_PMCTRL_WOW_PME_CLR;
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+ /*
+ * Setup for:
+ * - beacon misses
+ * - magic pattern
+ * - keep alive timeout
+ * - pattern matching
+ */
+
+ /*
+ * Program some default values for keep-alives, beacon misses, etc.
+ */
+ init_val = REG_READ(ah, AR_WOW_PATTERN_REG);
+ val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val;
+ REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
+ rval = REG_READ(ah, AR_WOW_PATTERN_REG);
+
+ init_val = REG_READ(ah, AR_WOW_COUNT_REG);
+ val = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | \
+ AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | \
+ AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
+ REG_WRITE(ah, AR_WOW_COUNT_REG, val);
+ rval = REG_READ(ah, AR_WOW_COUNT_REG);
+
+
+ init_val = REG_READ(ah, AR_WOW_BCN_TIMO_REG);
+ if (patternEnable & AH_WOW_BEACON_MISS)
+ val = AR_WOW_BEACON_TIMO;
+ else
+ /* We are not using the beacon miss. Program a large value. */
+ val = AR_WOW_BEACON_TIMO_MAX;
+ REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val);
+ rval = REG_READ(ah, AR_WOW_BCN_TIMO_REG);
+ if ((patternEnable & AH_WOW_BEACON_MISS) &&
+ !(rval & AR_WOW_BEACON_TIMO))
+ all_triggers_set = false;
+
+ init_val = REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
+
+ /*
+ * Keep Alive Timo in ms.
+ */
+ if (patternEnable == 0)
+ val = AR_WOW_KEEP_ALIVE_NEVER;
+ else
+ val = ka_timo * 32;
+ REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val);
+ rval = REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
+
+ init_val = REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
+ /*
+ * Keep Alive delay in us.
+ */
+ val = ka_delay * 1000;
+ REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val);
+ rval = REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
+
+ /*
+ * Create KeepAlive Pattern to respond to beacons.
+ */
+ ath9k_wow_create_keep_alive_pattern(ah);
+
+ /*
+ * Configure Mac Wow Registers.
+ */
+
+ val = REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
+ /*
+ * Send keep alive timeouts anyway.
+ */
+ val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
+
+ if (patternEnable & AH_WOW_LINK_CHANGE) {
+ val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS;
+ wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
+ } else
+ val |= AR_WOW_KEEP_ALIVE_FAIL_DIS;
+
+ REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val);
+ val = REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
+ if ((patternEnable & AH_WOW_LINK_CHANGE) &&
+ (val & AR_WOW_KEEP_ALIVE_FAIL_DIS))
+ all_triggers_set = false;
+ val = REG_READ(ah, AR_WOW_BCN_EN_REG);
+
+ /*
+ * We are relying on a bmiss failure. Ensure we have enough
+ * threshold to prevent false positives.
+ */
+ REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
+ AR_WOW_BMISSTHRESHOLD);
+
+ /*
+ * Beacon miss & user pattern events do not work on AR5416.
+ * We enable beacon miss wow pattern only for AR9280...
+ */
+ if (!AR_SREV_9280_10_OR_LATER(ah))
+ patternEnable &= ~AH_WOW_BEACON_MISS;
+
+ if (patternEnable & AH_WOW_BEACON_MISS) {
+ val |= AR_WOW_BEACON_FAIL_EN;
+ wow_event_mask |= AR_WOW_BEACON_FAIL;
+ } else
+ val &= ~AR_WOW_BEACON_FAIL_EN;
+
+ REG_WRITE(ah, AR_WOW_BCN_EN_REG, val);
+ val = REG_READ(ah, AR_WOW_BCN_EN_REG);
+ if ((patternEnable & AH_WOW_BEACON_MISS) &&
+ !(val & AR_WOW_BEACON_FAIL_EN))
+ all_triggers_set = false;
+
+ /*
+ * Enable the magic packet registers.
+ */
+ val = REG_READ(ah, AR_WOW_PATTERN_REG);
+ if (patternEnable & AH_WOW_MAGIC_PATTERN_EN) {
+ val |= AR_WOW_MAGIC_EN;
+ wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
+ } else
+ val &= ~AR_WOW_MAGIC_EN;
+
+ val |= AR_WOW_MAC_INTR_EN;
+ REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
+ val = REG_READ(ah, AR_WOW_PATTERN_REG);
+
+ /* Lets be a little more verbose about this one */
+ if (patternEnable & AH_WOW_MAGIC_PATTERN_EN) {
+ if (val & AR_WOW_MAGIC_EN) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGT,
+ "WoW: Magic pattern trigger set\n");
+ } else {
+ all_triggers_set = false;
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGT,
+ "WoW: Unable to enable magic "
+ "pattern trigger\n");
+ }
+ }
+
+ /*
+ * For AR9285 and later version of the chips
+ * enable wow pattern match for packets less than
+ * 256 bytes for all patterns.
+ */
+ if (AR_SREV_9285_10_OR_LATER(ah))
+ REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B_REG,
+ AR_WOW_PATTERN_SUPPORTED);
+
+ /*
+ * Set the power states appropriately and enable pme.
+ */
+ val = REG_READ(ah, AR_PCIE_PM_CTRL);
+ val |= AR_PMCTRL_PWR_STATE_D1D3 |
+ AR_PMCTRL_HOST_PME_EN |
+ AR_PMCTRL_PWR_PM_CTRL_ENA;
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+ ath9k_set_power_mode_wow_sleep(ah);
+
+ ah->ah_wowEventMask = wow_event_mask;
+
+ if (!all_triggers_set)
+ return -EIO;
+
+ return 0;
+}
+
+u32 ath9k_hw_wow_wake_up(struct ath_hw *ah)
+{
+ u32 wowStatus = 0;
+ u32 val = 0, rval;
+
+ /*
+ * Read the WOW Status register to know the wakeup reason.
+ */
+ rval = REG_READ(ah, AR_WOW_PATTERN_REG);
+ val = AR_WOW_STATUS(rval);
+
+ /*
+ * Mask only the WOW events that we have enabled. Sometimes, we have
+ * spurious WOW events from the AR_WOW_PATTERN_REG register. This mask
+ * will clean it up.
+ */
+ val &= ah->ah_wowEventMask;
+
+ if (val) {
+ if (val & AR_WOW_MAGIC_PAT_FOUND)
+ wowStatus |= AH_WOW_MAGIC_PATTERN_EN;
+ if (AR_WOW_PATTERN_FOUND(val))
+ wowStatus |= AH_WOW_USER_PATTERN_EN;
+ if (val & AR_WOW_KEEP_ALIVE_FAIL)
+ wowStatus |= AH_WOW_LINK_CHANGE;
+ if (val & AR_WOW_BEACON_FAIL)
+ wowStatus |= AH_WOW_BEACON_MISS;
+ }
+
+ /*
+ * Set and clear WOW_PME_CLEAR registers for the chip to generate next
+ * wow signal. Disable D3 before accessing other registers ?
+ */
+ val = REG_READ(ah, AR_PCIE_PM_CTRL);
+ /* Do we have to check the bit value 0x01000000 (7-10) ?? */
+ val &= ~AR_PMCTRL_PWR_STATE_D1D3;
+ val |= AR_PMCTRL_WOW_PME_CLR;
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+ /*
+ * Clear all events.
+ */
+ REG_WRITE(ah, AR_WOW_PATTERN_REG,
+ AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN_REG)));
+
+ /*
+ * Tie reset register.
+ * NB: Not tieing it back might have some repurcussions.
+ */
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_WRITE(ah, AR_WA, REG_READ(ah, AR_WA) |
+ AR_WA_UNTIE_RESET_EN |
+ AR_WA_POR_SHORT |
+ AR_WA_RESET_EN);
+ }
+
+ /* Restore the Beacon Threshold to init value */
+ REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
+
+ /*
+ * Restore the way the PCI-E Reset, Power-On-Reset, external
+ * PCIE_POR_SHORT pins are tied to its original value. Previously
+ * just before WOW sleep, we untie the PCI-E Reset to our Chip's
+ * Power On Reset so that any PCI-E reset from the bus will not
+ * reset our chip.
+ */
+ if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
+ ath9k_hw_configpcipowersave(ah, 0);
+
+ ah->ah_wowEventMask = 0;
+
+ return wowStatus;
+}
+
+void ath9k_wow_set_gpio_reset_low(struct ath_hw *ah)
+{
+ u32 val;
+
+ val = REG_READ(ah, AR_GPIO_OE_OUT);
+ val |= (1 << (2 * 2));
+ REG_WRITE(ah, AR_GPIO_OE_OUT, val);
+ val = REG_READ(ah, AR_GPIO_OE_OUT);
+ val = REG_READ(ah,AR_GPIO_IN_OUT );
+}
+
+const char *
+ath9k_hw_wow_event_to_string(u32 wow_event)
+{
+ if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
+ return "Magic pattern";
+ if (wow_event & AH_WOW_USER_PATTERN_EN)
+ return "User pattern";
+ if (wow_event & AH_WOW_LINK_CHANGE)
+ return "Link change";
+ if (wow_event & AH_WOW_BEACON_MISS)
+ return "Beacon miss";
+ return "Uknown event";
+}
+
+#endif /* CONFIG_PM */
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread