* [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback
2009-05-13 9:20 [RFC v2 0/5] WoW suport Luis R. Rodriguez
2009-05-13 9:20 ` [RFC v2 1/5] mac80211: fix idle trigger upon resume Luis R. Rodriguez
@ 2009-05-13 9:20 ` Luis R. Rodriguez
2009-05-13 9:53 ` Johannes Berg
2009-05-13 9:20 ` [RFC v2 3/5] cfg80211: add WoW support Luis R. Rodriguez
` (2 subsequent siblings)
4 siblings, 1 reply; 24+ messages in thread
From: Luis R. Rodriguez @ 2009-05-13 9:20 UTC (permalink / raw)
To: linux-wireless; +Cc: Luis R. Rodriguez
We inform the drivers so they can take appropriate actions during
suspend for WoW support.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/adm8211.c | 2 +-
drivers/net/wireless/at76c50x-usb.c | 2 +-
drivers/net/wireless/ath/ar9170/main.c | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 ++--
drivers/net/wireless/ath/ath9k/main.c | 2 +-
drivers/net/wireless/b43/main.c | 2 +-
drivers/net/wireless/b43legacy/main.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +-
drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +-
drivers/net/wireless/libertas_tf/main.c | 2 +-
drivers/net/wireless/mac80211_hwsim.c | 2 +-
drivers/net/wireless/mwl8k.c | 2 +-
drivers/net/wireless/p54/p54common.c | 2 +-
drivers/net/wireless/rt2x00/rt2x00.h | 2 +-
drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +-
drivers/net/wireless/rtl818x/rtl8180_dev.c | 2 +-
drivers/net/wireless/rtl818x/rtl8187_dev.c | 2 +-
drivers/net/wireless/wl12xx/main.c | 2 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 2 +-
include/net/mac80211.h | 12 +++++++-----
net/mac80211/driver-ops.h | 2 +-
21 files changed, 28 insertions(+), 26 deletions(-)
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index 316df03..bfcf9ce 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1546,7 +1546,7 @@ fail:
return retval;
}
-static void adm8211_stop(struct ieee80211_hw *dev)
+static void adm8211_stop(struct ieee80211_hw *dev, bool suspend)
{
struct adm8211_priv *priv = dev->priv;
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index e3caeef..e9ae9f7 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1766,7 +1766,7 @@ error:
return 0;
}
-static void at76_mac80211_stop(struct ieee80211_hw *hw)
+static void at76_mac80211_stop(struct ieee80211_hw *hw, bool suspend)
{
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..f85c981 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1003,7 +1003,7 @@ out:
return err;
}
-static void ar9170_op_stop(struct ieee80211_hw *hw)
+static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct ar9170 *ar = hw->priv;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index dbfe9f4..6a917ab 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -221,7 +221,7 @@ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
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, bool suspend);
static int ath5k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
static void ath5k_remove_interface(struct ieee80211_hw *hw,
@@ -2700,7 +2700,7 @@ 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, bool suspend)
{
ath5k_stop_hw(hw->priv);
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 7a1a8d3..fa1acf1 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2118,7 +2118,7 @@ exit:
return 0;
}
-static void ath9k_stop(struct ieee80211_hw *hw)
+static void ath9k_stop(struct ieee80211_hw *hw, bool suspend)
{
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..177db73 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4348,7 +4348,7 @@ 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, bool suspend)
{
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..7bac6be 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -3514,7 +3514,7 @@ out_mutex_unlock:
return err;
}
-static void b43legacy_op_stop(struct ieee80211_hw *hw)
+static void b43legacy_op_stop(struct ieee80211_hw *hw, bool suspend)
{
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 596977d..e48c345 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1985,7 +1985,7 @@ out:
return 0;
}
-static void iwl_mac_stop(struct ieee80211_hw *hw)
+static void iwl_mac_stop(struct ieee80211_hw *hw, bool suspend)
{
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 c32ec80..01b428d 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3397,7 +3397,7 @@ out_release_irq:
return ret;
}
-static void iwl3945_mac_stop(struct ieee80211_hw *hw)
+static void iwl3945_mac_stop(struct ieee80211_hw *hw, bool suspend)
{
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..07b2620 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -291,7 +291,7 @@ err_prog_firmware:
return ret;
}
-static void lbtf_op_stop(struct ieee80211_hw *hw)
+static void lbtf_op_stop(struct ieee80211_hw *hw, bool suspend)
{
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 61a4ad7..388068b 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -477,7 +477,7 @@ 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, bool suspend)
{
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 a263d5c..97f1c99 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -2916,7 +2916,7 @@ 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, bool suspend)
{
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..d6660af 100644
--- a/drivers/net/wireless/p54/p54common.c
+++ b/drivers/net/wireless/p54/p54common.c
@@ -2087,7 +2087,7 @@ out:
return err;
}
-static void p54_stop(struct ieee80211_hw *dev)
+static void p54_stop(struct ieee80211_hw *dev, bool suspend)
{
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..568a3c6 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -958,7 +958,7 @@ 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, bool suspend);
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..0540075 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -178,7 +178,7 @@ 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, bool suspend)
{
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..d72debc 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -627,7 +627,7 @@ 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, bool suspend)
{
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 6499ccc..878bb6a 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -991,7 +991,7 @@ 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, bool suspend)
{
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..dc3e804 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -363,7 +363,7 @@ out:
return ret;
}
-static void wl12xx_op_stop(struct ieee80211_hw *hw)
+static void wl12xx_op_stop(struct ieee80211_hw *hw, bool suspend)
{
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..e553a64 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -256,7 +256,7 @@ out:
return r;
}
-static void zd_op_stop(struct ieee80211_hw *hw)
+static void zd_op_stop(struct ieee80211_hw *hw, bool suspend)
{
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 d10ed17..9145cbf 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1296,10 +1296,12 @@ 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 with WoW we inform the driver
+ * whether or not the stop call is for suspend.
* Must be implemented.
*
* @add_interface: Called when a netdevice attached to the hardware is
@@ -1421,7 +1423,7 @@ 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, bool suspend);
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..ba6eca1 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -16,7 +16,7 @@ static inline int drv_start(struct ieee80211_local *local)
static inline void drv_stop(struct ieee80211_local *local)
{
- local->ops->stop(&local->hw);
+ local->ops->stop(&local->hw, local->suspended);
}
static inline int drv_add_interface(struct ieee80211_local *local,
--
1.6.0.6
^ permalink raw reply related [flat|nested] 24+ messages in thread* [RFC v2 3/5] cfg80211: add WoW support
2009-05-13 9:20 [RFC v2 0/5] WoW suport Luis R. Rodriguez
2009-05-13 9:20 ` [RFC v2 1/5] mac80211: fix idle trigger upon resume Luis R. Rodriguez
2009-05-13 9:20 ` [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback Luis R. Rodriguez
@ 2009-05-13 9:20 ` Luis R. Rodriguez
2009-05-13 9:55 ` Johannes Berg
2009-05-13 9:58 ` Johannes Berg
2009-05-13 9:21 ` [RFC v2 4/5] mac80211: " Luis R. Rodriguez
2009-05-13 9:21 ` [RFC v2 5/5] ath9k: Add Wake-on-Wireless-LAN support Luis R. Rodriguez
4 siblings, 2 replies; 24+ messages in thread
From: Luis R. Rodriguez @ 2009-05-13 9:20 UTC (permalink / raw)
To: linux-wireless; +Cc: Luis R. Rodriguez
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
include/linux/nl80211.h | 37 ++++++++++++++
include/net/cfg80211.h | 28 +++++++++++
net/wireless/nl80211.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 186 insertions(+), 0 deletions(-)
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 58c4ee1..5d9e469 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -240,6 +240,9 @@
* @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
* determined by the network interface.
*
+ * @NL80211_CMD_GET_WOW: get Wake-on-Wireless-LAN settings.
+ * @NL80211_CMD_SET_WOW: set Wake-on-Wireless-LAN settings.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -308,6 +311,9 @@ enum nl80211_commands {
NL80211_CMD_JOIN_IBSS,
NL80211_CMD_LEAVE_IBSS,
+ NL80211_CMD_GET_WOW,
+ NL80211_CMD_SET_WOW,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -327,6 +333,8 @@ enum nl80211_commands {
#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+#define NL80211_CMD_GET_WOW NL80211_CMD_GET_WOW
+#define NL80211_CMD_SET_WOW NL80211_CMD_SET_WOW
/**
* enum nl80211_attrs - nl80211 netlink attributes
@@ -499,6 +507,12 @@ enum nl80211_commands {
* this attribute can be used
* with %NL80211_CMD_ASSOCIATE request
*
+ * @NL80211_ATTR_WOW_TRIGGERS_SUPPORTED: the supported WoW triggers
+ * @NL80211_ATTR_WOW_TRIGGERS_ENABLED: used by %NL80211_CMD_SET_WOW to
+ * indicate which WoW triggers should be enabled. This is also
+ * used by %NL80211_CMD_GET_WOW to get the currently enabled WoW
+ * triggers.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -603,6 +617,9 @@ enum nl80211_attrs {
NL80211_ATTR_USE_MFP,
+ NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+ NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -1196,4 +1213,24 @@ enum nl80211_mfp {
NL80211_MFP_REQUIRED,
};
+/**
+ * enum nl80211_wow_triggers - Wake-on-Wireless-LAN triggers
+ *
+ * NL80211_WOW_TRIGGER_MAGIC_PACKET: a wake signal will be sent to the
+ * devices if a magic packet is received.
+ * NL80211_WOW_TRIGGER_BMISS: WoW signal will be sent to the device when
+ * a beacon has been missed by the associated AP.
+ * NL80211_WOW_TRIGGER_LINK_CHANGE: a wake signal will be sent to
+ * the device if a link change is detected on the device.
+ * NL80211_WOW_TRIGGER_USER_PATTERN: a wake sigal will be sent to the
+ * device if a user configurable pattern is received by
+ * the device.
+ */
+enum nl80211_wow_triggers {
+ NL80211_WOW_TRIGGER_MAGIC_PACKET = 1 << 0,
+ NL80211_WOW_TRIGGER_BMISS = 1 << 1,
+ NL80211_WOW_TRIGGER_LINK_CHANGE = 1 << 2,
+ NL80211_WOW_TRIGGER_PATTERN = 1 << 3,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5906440..5944707 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -765,6 +765,25 @@ enum wiphy_params_flags {
};
/**
+ * struct cfg80211_wow - Wake on Wireless-LAN support info
+ *
+ * This structure defines the WoW triggers supported by the device
+ * and also what specific WoW triggers the user wants enabled. To use
+ * WoW you can use standard ethernet utilities as you would with
+ * Wake-on-LAN.
+ *
+ * @triggers_supported: supported bitmask of WoW triggers.
+ * The flags for this bitmask are %NL80211_WOW_TRIGGER_*.
+ * @triggers_enabled: enabled triggers by the user. Default
+ * is to disable all triggers. The flags for this bitmask
+ * are %NL80211_WOW_TRIGGER_*.
+ */
+struct cfg80211_wow {
+ u32 triggers_supported;
+ u32 triggers_enabled;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -849,6 +868,8 @@ enum wiphy_params_flags {
* @changed bitfield (see &enum wiphy_params_flags) describes which values
* have changed. The actual parameter values are available in
* struct wiphy. If returning an error, no value should be changed.
+ *
+ * @set_wow: used to inform the device which WoW triggers should be enabled.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy);
@@ -940,6 +961,7 @@ struct cfg80211_ops {
int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
+ int (*set_wow)(struct wiphy *wiphy, u32 triggers);
};
/*
@@ -965,6 +987,11 @@ struct cfg80211_ops {
* channels at a later time. This can be used for devices which do not
* have calibration information gauranteed for frequencies or settings
* outside of its regulatory domain.
+ * @wow: Wake-on-Wireless-LAN configuration struct. The driver should
+ * set the capabilities before registering the wiphy. When we
+ * get a request to enable certain WoW events we will inform
+ * the driver through the set_wow() callback. If this is successfull
+ * we then set the passed triggers as enabled on the wiphy'w wow struct.
* @reg_notifier: the driver's regulatory notification callback
* @regd: the driver's regulatory domain, if one was requested via
* the regulatory_hint() API. This can be used by the driver
@@ -992,6 +1019,7 @@ struct wiphy {
bool strict_regulatory;
enum cfg80211_signal_type signal_type;
+ struct cfg80211_wow wow;
int bss_priv_size;
u8 max_scan_ssids;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0c703b6..25fa6bd 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -123,6 +123,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
[NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
[NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
+
+ [NL80211_ATTR_WOW_TRIGGERS_SUPPORTED] = { .type = NLA_U32 },
+ [NL80211_ATTR_WOW_TRIGGERS_ENABLED] = { .type = NLA_U32 },
};
/* IE validation */
@@ -3270,6 +3273,112 @@ unlock_rtnl:
return err;
}
+static int nl80211_get_wow(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct wiphy *wiphy;
+ int err;
+ void *hdr;
+ struct sk_buff *msg;
+
+ rtnl_lock();
+
+ /* Look up our device */
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto out_rtnl;
+
+ wiphy = &drv->wiphy;
+
+ /* Draw up a netlink message to send back */
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ err = -ENOBUFS;
+ goto out;
+ }
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_GET_WOW);
+ if (!hdr)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+ wiphy->wow.triggers_supported);
+ NLA_PUT_U32(msg, NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+ wiphy->wow.triggers_enabled);
+
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ err = -EMSGSIZE;
+ out:
+ /* Cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
+static int nl80211_set_wow(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct wiphy *wiphy;
+ u32 triggers_requested;
+
+ if (!info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED])
+ return -EINVAL;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto out_rtnl;
+
+ if (!drv->ops->set_wow) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+
+ if (!wiphy->wow.triggers_supported) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ triggers_requested =
+ nla_get_u32(info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED]);
+
+ if (!(wiphy->wow.triggers_supported & triggers_requested)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Apply changes */
+ err = drv->ops->set_wow(&drv->wiphy, triggers_requested);
+
+ if (err)
+ goto out;
+
+ wiphy->wow.triggers_enabled = triggers_requested;
+
+ out:
+ /* cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -3483,6 +3592,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_GET_WOW,
+ .doit = nl80211_get_wow,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_WOW,
+ .doit = nl80211_set_wow,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
.name = "mlme",
--
1.6.0.6
^ permalink raw reply related [flat|nested] 24+ messages in thread* [RFC v2 5/5] ath9k: Add Wake-on-Wireless-LAN support
2009-05-13 9:20 [RFC v2 0/5] WoW suport Luis R. Rodriguez
` (3 preceding siblings ...)
2009-05-13 9:21 ` [RFC v2 4/5] mac80211: " Luis R. Rodriguez
@ 2009-05-13 9:21 ` Luis R. Rodriguez
4 siblings, 0 replies; 24+ messages in thread
From: Luis R. Rodriguez @ 2009-05-13 9:21 UTC (permalink / raw)
To: linux-wireless; +Cc: Luis R. Rodriguez
This adds WoWLAN suppport for ath9k. 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 | 17 +
drivers/net/wireless/ath/ath9k/debug.c | 1 -
drivers/net/wireless/ath/ath9k/debug.h | 1 +
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 | 105 ++++++
drivers/net/wireless/ath/ath9k/reg.h | 154 ++++++++
drivers/net/wireless/ath/ath9k/wow.c | 543 +++++++++++++++++++++++++++++
11 files changed, 916 insertions(+), 1 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..34aebc6 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -599,12 +599,29 @@ struct ath_softc {
struct ath_rfkill rf_kill;
struct ath_ani ani;
struct ath9k_node_stats nodestats;
+#ifdef CONFIG_PM
+ 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..43ddce7 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -493,7 +493,6 @@ static const struct file_operations fops_wiphy = {
.owner = THIS_MODULE
};
-
int ath9k_init_debug(struct ath_softc *sc)
{
sc->debug.debug_mask = ath9k_debug;
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 23298b9..29ec232 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
};
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 4acfab5..719b31d 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 dd8508e..77a7346 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..0cd23bb 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 AR9280 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 fa1acf1..5186d3a 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
@@ -2122,9 +2134,18 @@ static void ath9k_stop(struct ieee80211_hw *hw, bool suspend)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct wiphy *wiphy = sc->hw->wiphy;
aphy->state = ATH_WIPHY_INACTIVE;
+#ifdef CONFIG_PM
+ if (suspend && wiphy->wow.triggers_enabled) {
+ DPRINTF(sc, ATH_DBG_ANY, "Leaving radio on during "
+ "suspend 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..5dfb49d 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");
@@ -183,6 +186,12 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->irq = pdev->irq;
+ /* Only for PCI */
+ hw->wiphy->wow.triggers_supported =
+ NL80211_WOW_TRIGGER_MAGIC_PACKET |
+ NL80211_WOW_TRIGGER_BMISS |
+ NL80211_WOW_TRIGGER_LINK_CHANGE;
+
ah = sc->sc_ah;
printk(KERN_INFO
"%s: Atheros AR%s MAC/BB Rev:%x "
@@ -219,11 +228,67 @@ static void ath_pci_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM
+static u32 ath9k_wow_event_map(u32 cfg_wow_events)
+{
+ u32 wow_events = 0;
+
+ if (cfg_wow_events & NL80211_WOW_TRIGGER_MAGIC_PACKET)
+ wow_events |= AH_WOW_MAGIC_PATTERN_EN;
+ if (cfg_wow_events & NL80211_WOW_TRIGGER_BMISS)
+ wow_events |= AH_WOW_BEACON_MISS;
+ if (cfg_wow_events & NL80211_WOW_TRIGGER_LINK_CHANGE)
+ wow_events |= AH_WOW_LINK_CHANGE;
+
+ return wow_events;
+}
+
+static void ath9k_pci_wow_enable(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct wiphy *wiphy = sc->hw->wiphy;
+ int r;
+ u32 wake_up_events;
+
+ wake_up_events = ath9k_wow_event_map(wiphy->wow.triggers_enabled);
+
+ /* 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;
+}
+
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;
+ struct wiphy *wiphy = sc->hw->wiphy;
+
+ if (wiphy->wow.triggers_enabled && device_can_wakeup(&pdev->dev))
+ ath9k_pci_wow_enable(sc);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
@@ -234,16 +299,53 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
pci_save_state(pdev);
pci_disable_device(pdev);
+ if (wiphy->wow.triggers_enabled && device_can_wakeup(&pdev->dev)) {
+ pci_prepare_to_sleep(pdev);
+ return 0;
+ }
+ pci_wake_from_d3(pdev, !!wiphy->wow.triggers_enabled);
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;
+
+ 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);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct wiphy *wiphy = sc->hw->wiphy;
int err;
err = pci_enable_device(pdev);
@@ -251,6 +353,9 @@ static int ath_pci_resume(struct pci_dev *pdev)
return err;
pci_restore_state(pdev);
+ if (wiphy->wow.triggers_enabled)
+ 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] 24+ messages in thread