Commit 6d5a748d authored by Ram Amrani's avatar Ram Amrani Committed by Kalle Valo

wlcore: add ability to reduce FW interrupts during suspend

Add the ability to mask FW interrupts on RX BA activity, PSM
entry/exit and fast-link notifications. This is used when the host
is suspended in order to decrease redundant wake ups.
Signed-off-by: default avatarRam Amrani <ramrani@ti.com>
Signed-off-by: default avatarArik Nemtsov <arik@wizery.com>
Signed-off-by: default avatarEliad Peller <eliad@wizery.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 7d3b29e5
...@@ -250,6 +250,7 @@ static struct wlcore_conf wl12xx_conf = { ...@@ -250,6 +250,7 @@ static struct wlcore_conf wl12xx_conf = {
.keep_alive_interval = 55000, .keep_alive_interval = 55000,
.max_listen_interval = 20, .max_listen_interval = 20,
.sta_sleep_auth = WL1271_PSM_ILLEGAL, .sta_sleep_auth = WL1271_PSM_ILLEGAL,
.suspend_rx_ba_activity = 0,
}, },
.itrim = { .itrim = {
.enable = false, .enable = false,
...@@ -1728,6 +1729,8 @@ static struct wlcore_ops wl12xx_ops = { ...@@ -1728,6 +1729,8 @@ static struct wlcore_ops wl12xx_ops = {
.convert_hwaddr = wl12xx_convert_hwaddr, .convert_hwaddr = wl12xx_convert_hwaddr,
.lnk_high_prio = wl12xx_lnk_high_prio, .lnk_high_prio = wl12xx_lnk_high_prio,
.lnk_low_prio = wl12xx_lnk_low_prio, .lnk_low_prio = wl12xx_lnk_low_prio,
.interrupt_notify = NULL,
.rx_ba_filter = NULL,
}; };
static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
......
...@@ -194,3 +194,59 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl, ...@@ -194,3 +194,59 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
kfree(acx); kfree(acx);
return ret; return ret;
} }
/*
* When the host is suspended, we don't want to get any fast-link/PSM
* notifications
*/
int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl,
bool action)
{
struct wl18xx_acx_interrupt_notify *acx;
int ret = 0;
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
acx->enable = action;
ret = wl1271_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx interrupt notify setting failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
/*
* When the host is suspended, we can configure the FW to disable RX BA
* notifications.
*/
int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action)
{
struct wl18xx_acx_rx_ba_filter *acx;
int ret = 0;
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
acx->enable = (u32)action;
ret = wl1271_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx rx ba activity filter setting failed: %d",
ret);
goto out;
}
out:
kfree(acx);
return ret;
}
...@@ -32,7 +32,10 @@ enum { ...@@ -32,7 +32,10 @@ enum {
ACX_SIM_CONFIG = 0x0053, ACX_SIM_CONFIG = 0x0053,
ACX_CLEAR_STATISTICS = 0x0054, ACX_CLEAR_STATISTICS = 0x0054,
ACX_AUTO_RX_STREAMING = 0x0055, ACX_AUTO_RX_STREAMING = 0x0055,
ACX_PEER_CAP = 0x0056 ACX_PEER_CAP = 0x0056,
ACX_INTERRUPT_NOTIFY = 0x0057,
ACX_RX_BA_FILTER = 0x0058
}; };
/* numbers of bits the length field takes (add 1 for the actual number) */ /* numbers of bits the length field takes (add 1 for the actual number) */
...@@ -326,6 +329,24 @@ struct wlcore_acx_peer_cap { ...@@ -326,6 +329,24 @@ struct wlcore_acx_peer_cap {
u8 padding; u8 padding;
} __packed; } __packed;
/*
* ACX_INTERRUPT_NOTIFY
* enable/disable fast-link/PSM notification from FW
*/
struct wl18xx_acx_interrupt_notify {
struct acx_header header;
u32 enable;
};
/*
* ACX_RX_BA_FILTER
* enable/disable RX BA filtering in FW
*/
struct wl18xx_acx_rx_ba_filter {
struct acx_header header;
u32 enable;
};
int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
u32 sdio_blk_size, u32 extra_mem_blks, u32 sdio_blk_size, u32 extra_mem_blks,
u32 len_field_size); u32 len_field_size);
...@@ -336,5 +357,7 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl, ...@@ -336,5 +357,7 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation, bool allow_ht_operation,
u32 rate_set, u8 hlid); u32 rate_set, u8 hlid);
int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, bool action);
int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action);
#endif /* __WL18XX_ACX_H__ */ #endif /* __WL18XX_ACX_H__ */
...@@ -378,6 +378,7 @@ static struct wlcore_conf wl18xx_conf = { ...@@ -378,6 +378,7 @@ static struct wlcore_conf wl18xx_conf = {
.keep_alive_interval = 55000, .keep_alive_interval = 55000,
.max_listen_interval = 20, .max_listen_interval = 20,
.sta_sleep_auth = WL1271_PSM_ILLEGAL, .sta_sleep_auth = WL1271_PSM_ILLEGAL,
.suspend_rx_ba_activity = 0,
}, },
.itrim = { .itrim = {
.enable = false, .enable = false,
...@@ -1693,6 +1694,8 @@ static struct wlcore_ops wl18xx_ops = { ...@@ -1693,6 +1694,8 @@ static struct wlcore_ops wl18xx_ops = {
.smart_config_start = wl18xx_cmd_smart_config_start, .smart_config_start = wl18xx_cmd_smart_config_start,
.smart_config_stop = wl18xx_cmd_smart_config_stop, .smart_config_stop = wl18xx_cmd_smart_config_stop,
.smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key, .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key,
.interrupt_notify = wl18xx_acx_interrupt_notify_config,
.rx_ba_filter = wl18xx_acx_rx_ba_filter,
}; };
/* HT cap appropriate for wide channels in 2Ghz */ /* HT cap appropriate for wide channels in 2Ghz */
......
...@@ -997,6 +997,11 @@ struct conf_conn_settings { ...@@ -997,6 +997,11 @@ struct conf_conn_settings {
* whether we can go to ELP. * whether we can go to ELP.
*/ */
u8 sta_sleep_auth; u8 sta_sleep_auth;
/*
* Default RX BA Activity filter configuration
*/
u8 suspend_rx_ba_activity;
} __packed; } __packed;
enum { enum {
...@@ -1347,7 +1352,7 @@ struct conf_recovery_settings { ...@@ -1347,7 +1352,7 @@ struct conf_recovery_settings {
* version, the two LSB are the lower driver's private conf * version, the two LSB are the lower driver's private conf
* version. * version.
*/ */
#define WLCORE_CONF_VERSION (0x0005 << 16) #define WLCORE_CONF_VERSION (0x0006 << 16)
#define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_MASK 0xffff0000
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
sizeof(struct wlcore_conf)) sizeof(struct wlcore_conf))
......
...@@ -217,6 +217,22 @@ wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -217,6 +217,22 @@ wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif)
wl->ops->sta_rc_update(wl, wlvif); wl->ops->sta_rc_update(wl, wlvif);
} }
static inline int
wlcore_hw_interrupt_notify(struct wl1271 *wl, bool action)
{
if (wl->ops->interrupt_notify)
return wl->ops->interrupt_notify(wl, action);
return 0;
}
static inline int
wlcore_hw_rx_ba_filter(struct wl1271 *wl, bool action)
{
if (wl->ops->rx_ba_filter)
return wl->ops->rx_ba_filter(wl, action);
return 0;
}
static inline int static inline int
wlcore_hw_set_peer_cap(struct wl1271 *wl, wlcore_hw_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_sta_ht_cap *ht_cap,
......
...@@ -1685,19 +1685,15 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, ...@@ -1685,19 +1685,15 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
goto out; goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wl1271_configure_wowlan(wl, wow); ret = wl1271_configure_wowlan(wl, wow);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
if ((wl->conf.conn.suspend_wake_up_event == if ((wl->conf.conn.suspend_wake_up_event ==
wl->conf.conn.wake_up_event) && wl->conf.conn.wake_up_event) &&
(wl->conf.conn.suspend_listen_interval == (wl->conf.conn.suspend_listen_interval ==
wl->conf.conn.listen_interval)) wl->conf.conn.listen_interval))
goto out_sleep; goto out;
ret = wl1271_acx_wake_up_conditions(wl, wlvif, ret = wl1271_acx_wake_up_conditions(wl, wlvif,
wl->conf.conn.suspend_wake_up_event, wl->conf.conn.suspend_wake_up_event,
...@@ -1705,9 +1701,6 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, ...@@ -1705,9 +1701,6 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
if (ret < 0) if (ret < 0)
wl1271_error("suspend: set wake up conditions failed: %d", ret); wl1271_error("suspend: set wake up conditions failed: %d", ret);
out_sleep:
wl1271_ps_elp_sleep(wl);
out: out:
return ret; return ret;
...@@ -1721,13 +1714,8 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl, ...@@ -1721,13 +1714,8 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl,
if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
goto out; goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
wl1271_ps_elp_sleep(wl);
out: out:
return ret; return ret;
...@@ -1756,10 +1744,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -1756,10 +1744,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
return; return;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
return;
if (is_sta) { if (is_sta) {
wl1271_configure_wowlan(wl, NULL); wl1271_configure_wowlan(wl, NULL);
...@@ -1767,7 +1751,7 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -1767,7 +1751,7 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
wl->conf.conn.wake_up_event) && wl->conf.conn.wake_up_event) &&
(wl->conf.conn.suspend_listen_interval == (wl->conf.conn.suspend_listen_interval ==
wl->conf.conn.listen_interval)) wl->conf.conn.listen_interval))
goto out_sleep; return;
ret = wl1271_acx_wake_up_conditions(wl, wlvif, ret = wl1271_acx_wake_up_conditions(wl, wlvif,
wl->conf.conn.wake_up_event, wl->conf.conn.wake_up_event,
...@@ -1780,9 +1764,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -1780,9 +1764,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
} else if (is_ap) { } else if (is_ap) {
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
} }
out_sleep:
wl1271_ps_elp_sleep(wl);
} }
static int wl1271_op_suspend(struct ieee80211_hw *hw, static int wl1271_op_suspend(struct ieee80211_hw *hw,
...@@ -1804,6 +1785,11 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, ...@@ -1804,6 +1785,11 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
wl1271_tx_flush(wl); wl1271_tx_flush(wl);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
return ret;
wl->wow_enabled = true; wl->wow_enabled = true;
wl12xx_for_each_wlvif(wl, wlvif) { wl12xx_for_each_wlvif(wl, wlvif) {
ret = wl1271_configure_suspend(wl, wlvif, wow); ret = wl1271_configure_suspend(wl, wlvif, wow);
...@@ -1813,7 +1799,27 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, ...@@ -1813,7 +1799,27 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
return ret; return ret;
} }
} }
/* disable fast link flow control notifications from FW */
ret = wlcore_hw_interrupt_notify(wl, false);
if (ret < 0)
goto out_sleep;
/* if filtering is enabled, configure the FW to drop all RX BA frames */
ret = wlcore_hw_rx_ba_filter(wl,
!!wl->conf.conn.suspend_rx_ba_activity);
if (ret < 0)
goto out_sleep;
out_sleep:
wl1271_ps_elp_sleep(wl);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
if (ret < 0) {
wl1271_warning("couldn't prepare device to suspend");
return ret;
}
/* flush any remaining work */ /* flush any remaining work */
wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
...@@ -1887,13 +1893,29 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) ...@@ -1887,13 +1893,29 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
if (pending_recovery) { if (pending_recovery) {
wl1271_warning("queuing forgotten recovery on resume"); wl1271_warning("queuing forgotten recovery on resume");
ieee80211_queue_work(wl->hw, &wl->recovery_work); ieee80211_queue_work(wl->hw, &wl->recovery_work);
goto out; goto out_sleep;
} }
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl12xx_for_each_wlvif(wl, wlvif) { wl12xx_for_each_wlvif(wl, wlvif) {
wl1271_configure_resume(wl, wlvif); wl1271_configure_resume(wl, wlvif);
} }
ret = wlcore_hw_interrupt_notify(wl, true);
if (ret < 0)
goto out_sleep;
/* if filtering is enabled, configure the FW to drop all RX BA frames */
ret = wlcore_hw_rx_ba_filter(wl, false);
if (ret < 0)
goto out_sleep;
out_sleep:
wl1271_ps_elp_sleep(wl);
out: out:
wl->wow_enabled = false; wl->wow_enabled = false;
......
...@@ -116,6 +116,8 @@ struct wlcore_ops { ...@@ -116,6 +116,8 @@ struct wlcore_ops {
struct wl1271_link *lnk); struct wl1271_link *lnk);
bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk); struct wl1271_link *lnk);
int (*interrupt_notify)(struct wl1271 *wl, bool action);
int (*rx_ba_filter)(struct wl1271 *wl, bool action);
int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap); int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap);
int (*smart_config_stop)(struct wl1271 *wl); int (*smart_config_stop)(struct wl1271 *wl);
int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment