summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2011-07-13 23:03:17 +0200
committerPhilippe LANGLAIS <philippe.langlais@stericsson.com>2011-10-13 09:44:11 +0200
commit8777e41f161c3a785d04d439ceec2657492802ba (patch)
treeda92b69529522b14ca0561e272a7dd5066690111
parent7ae20d2e8db0776aa8b0855d11c637e3476e46c5 (diff)
cw1200: Wakeup on wireless (WoW) is implemented.
+ Core and SDIO power management is implemented as defined in wireless-next v3.0-rc4. + SDD is cached to avoid disk access at suspend/resume time. TODO: - WoW conditions and filtering are not yet implemented. - BUG: Late interrupts (coming after suspend() callback but before the actual suspend) are not detected as wakeup sources. Universal (Android/GLK) way of waking up halfly-suspended system to be found. Signed-off-by: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> Change-Id: Ib4931a261e592f2927455e988055cd673250ec81 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27297 Tested-by: Bartosz MARKOWSKI <bartosz.markowski@tieto.com> Reviewed-by: Bartosz MARKOWSKI <bartosz.markowski@tieto.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33484 Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
-rw-r--r--drivers/staging/cw1200/Makefile3
-rw-r--r--drivers/staging/cw1200/bh.c70
-rw-r--r--drivers/staging/cw1200/bh.h2
-rw-r--r--drivers/staging/cw1200/cw1200.h17
-rw-r--r--drivers/staging/cw1200/cw1200_plat.h5
-rw-r--r--drivers/staging/cw1200/cw1200_sdio.c45
-rw-r--r--drivers/staging/cw1200/main.c25
-rw-r--r--drivers/staging/cw1200/pm.c142
-rw-r--r--drivers/staging/cw1200/pm.h23
-rw-r--r--drivers/staging/cw1200/sbus.h1
-rw-r--r--drivers/staging/cw1200/sta.c58
-rw-r--r--drivers/staging/cw1200/sta.h1
-rw-r--r--drivers/staging/cw1200/wsm.c4
13 files changed, 329 insertions, 67 deletions
diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile
index fae984d10ce..c0e88bd0f11 100644
--- a/drivers/staging/cw1200/Makefile
+++ b/drivers/staging/cw1200/Makefile
@@ -9,7 +9,8 @@ cw1200_core-y := \
sta.o \
ap.o \
scan.o
-cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o
+cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o
+cw1200_core-$(CONFIG_PM) += pm.o
cw1200_wlan-y := cw1200_sdio.o
diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c
index d49f9fdf732..2d438a96e38 100644
--- a/drivers/staging/cw1200/bh.c
+++ b/drivers/staging/cw1200/bh.c
@@ -39,6 +39,14 @@ static int cw1200_bh(void *arg);
#define PIGGYBACK_CTRL_REG (2)
#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+/* Suspend state privates */
+enum cw1200_bh_pm_state {
+ CW1200_BH_RESUMED = 0,
+ CW1200_BH_SUSPEND,
+ CW1200_BH_SUSPENDED,
+ CW1200_BH_RESUME,
+};
+
typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
u8 *data, size_t size);
@@ -52,10 +60,11 @@ int cw1200_register_bh(struct cw1200_common *priv)
atomic_set(&priv->bh_rx, 0);
atomic_set(&priv->bh_tx, 0);
atomic_set(&priv->bh_term, 0);
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
priv->buf_id_tx = 0;
priv->buf_id_rx = 0;
init_waitqueue_head(&priv->bh_wq);
- init_waitqueue_head(&priv->hw_bufs_used_wq);
+ init_waitqueue_head(&priv->bh_evt_wq);
priv->bh_thread = kthread_create(&cw1200_bh, priv, "cw1200_bh");
if (IS_ERR(priv->bh_thread)) {
err = PTR_ERR(priv->bh_thread);
@@ -107,6 +116,30 @@ void cw1200_bh_wakeup(struct cw1200_common *priv)
wake_up_interruptible(&priv->bh_wq);
}
+void cw1200_bh_suspend(struct cw1200_common *priv)
+{
+ bh_printk(KERN_DEBUG "[BH] suspend.\n");
+ if (WARN_ON(priv->bh_error))
+ return;
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
+ wake_up_interruptible(&priv->bh_wq);
+ wait_event_interruptible(priv->bh_evt_wq, priv->bh_error ||
+ (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)));
+}
+
+void cw1200_bh_resume(struct cw1200_common *priv)
+{
+ bh_printk(KERN_DEBUG "[BH] resume.\n");
+ if (WARN_ON(priv->bh_error))
+ return;
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
+ wake_up_interruptible(&priv->bh_wq);
+ wait_event_interruptible(priv->bh_evt_wq, priv->bh_error ||
+ (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)));
+}
+
static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
{
++priv->hw_bufs_used;
@@ -123,7 +156,7 @@ int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs)
ret = 1;
if (!priv->hw_bufs_used)
- wake_up_interruptible(&priv->hw_bufs_used_wq);
+ wake_up_interruptible(&priv->bh_evt_wq);
return ret;
}
@@ -221,7 +254,7 @@ static int cw1200_bh(void *arg)
struct cw1200_common *priv = arg;
struct sk_buff *skb_rx = NULL;
size_t read_len = 0;
- int rx, tx, term;
+ int rx, tx, term, suspend;
struct wsm_hdr *wsm;
size_t wsm_len;
int wsm_id;
@@ -244,7 +277,8 @@ static int cw1200_bh(void *arg)
rx = atomic_xchg(&priv->bh_rx, 0);
tx = atomic_xchg(&priv->bh_tx, 0);
term = atomic_xchg(&priv->bh_term, 0);
- (rx || tx || term);
+ suspend = atomic_read(&priv->bh_suspend);
+ (rx || tx || term || suspend);
}), status);
if (status < 0 || term)
@@ -252,9 +286,35 @@ static int cw1200_bh(void *arg)
if (!status) {
bh_printk(KERN_DEBUG "[BH] Device wakedown.\n");
- WARN_ON(cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0));
+ WARN_ON(cw1200_reg_write_16(priv,
+ ST90TDS_CONTROL_REG_ID, 0));
priv->device_can_sleep = true;
continue;
+ } else if (suspend) {
+ bh_printk(KERN_DEBUG "[BH] Device suspend.\n");
+ if (priv->powersave_enabled &&
+ !priv->device_can_sleep) {
+ WARN_ON(cw1200_reg_write_16(priv,
+ ST90TDS_CONTROL_REG_ID, 0));
+ priv->device_can_sleep = true;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
+ wake_up_interruptible(&priv->bh_evt_wq);
+ status = wait_event_interruptible(priv->bh_wq,
+ CW1200_BH_RESUME == atomic_read(
+ &priv->bh_suspend));
+ if (status < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: Failed to wait for resume: %ld.\n",
+ __func__, status);
+ break;
+ }
+ bh_printk(KERN_DEBUG "[BH] Device resume.\n");
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+ wake_up_interruptible(&priv->bh_evt_wq);
+ atomic_add(1, &priv->bh_rx);
+ continue;
}
tx += pending_tx;
diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h
index 14b641fbe1d..a19393c61dd 100644
--- a/drivers/staging/cw1200/bh.h
+++ b/drivers/staging/cw1200/bh.h
@@ -22,6 +22,8 @@ int cw1200_register_bh(struct cw1200_common *priv);
void cw1200_unregister_bh(struct cw1200_common *priv);
void cw1200_irq_handler(struct cw1200_common *priv);
void cw1200_bh_wakeup(struct cw1200_common *priv);
+void cw1200_bh_suspend(struct cw1200_common *priv);
+void cw1200_bh_resume(struct cw1200_common *priv);
/* Must be called from BH thread. */
void cw1200_enable_powersave(struct cw1200_common *priv,
bool enable);
diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h
index 97afb8c1392..68d82874765 100644
--- a/drivers/staging/cw1200/cw1200.h
+++ b/drivers/staging/cw1200/cw1200.h
@@ -33,6 +33,8 @@
/* extern */ struct sbus_ops;
/* extern */ struct task_struct;
/* extern */ struct cw1200_debug_priv;
+/* extern */ struct cw1200_suspend_state;
+/* extern */ struct firmware;
#if defined(CONFIG_CW1200_TXRX_DEBUG)
#define txrx_printk(...) printk(__VA_ARGS__)
@@ -81,6 +83,7 @@ struct cw1200_common {
/* calibration, output power limit and rssi<->dBm conversation data */
/* BBP/MAC state */
+ const struct firmware *sdd;
struct ieee80211_rate *rates;
struct ieee80211_rate *mcs_rates;
u8 mac_addr[ETH_ALEN];
@@ -110,20 +113,22 @@ struct cw1200_common {
struct wsm_beacon_filter_control bf_control;
u8 ba_tid_mask;
struct wsm_multicast_filter multicast_filter;
+ struct cw1200_suspend_state *suspend_state;
/* BH */
atomic_t bh_rx;
atomic_t bh_tx;
atomic_t bh_term;
+ atomic_t bh_suspend;
struct task_struct *bh_thread;
int bh_error;
wait_queue_head_t bh_wq;
+ wait_queue_head_t bh_evt_wq;
int buf_id_tx; /* byte */
int buf_id_rx; /* byte */
int wsm_rx_seq; /* byte */
int wsm_tx_seq; /* byte */
int hw_bufs_used;
- wait_queue_head_t hw_bufs_used_wq;
struct sk_buff *skb_cache;
bool powersave_enabled;
bool device_can_sleep;
@@ -194,11 +199,11 @@ struct cw1200_sta_priv {
};
/* interfaces for the drivers */
-int cw1200_probe(const struct sbus_ops *sbus_ops,
- struct sbus_priv *sbus,
- struct device *pdev,
- struct cw1200_common **pself);
-void cw1200_release(struct cw1200_common *self);
+int cw1200_core_probe(const struct sbus_ops *sbus_ops,
+ struct sbus_priv *sbus,
+ struct device *pdev,
+ struct cw1200_common **pself);
+void cw1200_core_release(struct cw1200_common *self);
#define CW1200_DBG_MSG 0x00000001
#define CW1200_DBG_NIY 0x00000002
diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h
index ed354de0edf..e79794f42a3 100644
--- a/drivers/staging/cw1200/cw1200_plat.h
+++ b/drivers/staging/cw1200/cw1200_plat.h
@@ -5,6 +5,9 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#ifndef CW1200_PLAT_H_INCLUDED
+#define CW1200_PLAT_H_INCLUDED
+
#include <linux/ioport.h>
struct cw1200_platform_data {
@@ -17,3 +20,5 @@ struct cw1200_platform_data {
/* Declaration only. Should be implemented in arch/xxx/mach-yyy */
const struct cw1200_platform_data *cw1200_get_platform_data(void);
+
+#endif /* CW1200_PLAT_H_INCLUDED */
diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c
index ae07267dc99..cf5c5c185be 100644
--- a/drivers/staging/cw1200/cw1200_sdio.c
+++ b/drivers/staging/cw1200/cw1200_sdio.c
@@ -39,7 +39,7 @@ struct sbus_priv {
void *irq_priv;
};
-static const struct sdio_device_id if_sdio_ids[] = {
+static const struct sdio_device_id cw1200_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) },
{ /* end: all zeroes */ },
};
@@ -52,7 +52,8 @@ static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self,
{
int ret = sdio_memcpy_fromio(self->func, dst, addr, count);
if (ret) {
- printk(KERN_ERR "!!! Can't read %d bytes from 0x%.8X. Err %d.\n",
+ printk(KERN_ERR "!!! Can't read %d bytes from 0x%.8X."
+ " Err %d.\n",
count, addr, ret);
}
return ret;
@@ -111,10 +112,6 @@ static int cw1200_request_irq(struct sbus_priv *self,
if (WARN_ON(ret < 0))
goto exit;
- ret = enable_irq_wake(irq->start);
- if (WARN_ON(ret))
- goto free_irq;
-
/* Hack to access Fuction-0 */
func_num = self->func->num;
self->func->num = 0;
@@ -139,8 +136,6 @@ static int cw1200_request_irq(struct sbus_priv *self,
set_func:
self->func->num = func_num;
- disable_irq_wake(irq->start);
-free_irq:
free_irq(irq->start, self);
exit:
return ret;
@@ -191,7 +186,6 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self)
ret = sdio_release_irq(self->func);
sdio_release_host(self->func);
#else
- disable_irq_wake(irq->start);
free_irq(irq->start, self);
#endif
@@ -283,7 +277,7 @@ static int cw1200_sdio_reset(struct sbus_priv *self)
return 0;
}
-static size_t cw1200_align_size(struct sbus_priv *self, size_t size)
+static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size)
{
size_t aligned = sdio_align_size(self->func, size);
/* HACK!!! Problems with DMA size on u8500 platform */
@@ -295,6 +289,25 @@ static size_t cw1200_align_size(struct sbus_priv *self, size_t size)
return aligned;
}
+static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend)
+{
+ int ret;
+ const struct resource *irq = self->pdata->irq;
+ struct sdio_func *func = self->func;
+
+ sdio_claim_host(func);
+ if (suspend)
+ ret = mmc_host_disable(func->card->host);
+ else
+ ret = mmc_host_enable(func->card->host);
+ sdio_release_host(func);
+
+ if (!ret && irq)
+ ret = set_irq_wake(irq->start, suspend);
+
+ return ret;
+}
+
static struct sbus_ops cw1200_sdio_sbus_ops = {
.sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
.sbus_memcpy_toio = cw1200_sdio_memcpy_toio,
@@ -303,7 +316,8 @@ static struct sbus_ops cw1200_sdio_sbus_ops = {
.irq_subscribe = cw1200_sdio_irq_subscribe,
.irq_unsubscribe = cw1200_sdio_irq_unsubscribe,
.reset = cw1200_sdio_reset,
- .align_size = cw1200_align_size,
+ .align_size = cw1200_sdio_align_size,
+ .power_mgmt = cw1200_sdio_pm,
};
/* Probe Function to be called by SDIO stack when device is discovered */
@@ -329,7 +343,7 @@ static int cw1200_sdio_probe(struct sdio_func *func,
sdio_enable_func(func);
sdio_release_host(func);
- status = cw1200_probe(&cw1200_sdio_sbus_ops,
+ status = cw1200_core_probe(&cw1200_sdio_sbus_ops,
self, &func->dev, &self->core);
if (status) {
sdio_claim_host(func);
@@ -342,14 +356,15 @@ static int cw1200_sdio_probe(struct sdio_func *func,
return status;
}
-/* Disconnect Function to be called by SDIO stack when device is disconnected */
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected */
static void cw1200_sdio_disconnect(struct sdio_func *func)
{
struct sbus_priv *self = sdio_get_drvdata(func);
if (self) {
if (self->core) {
- cw1200_release(self->core);
+ cw1200_core_release(self->core);
self->core = NULL;
}
sdio_claim_host(func);
@@ -362,7 +377,7 @@ static void cw1200_sdio_disconnect(struct sdio_func *func)
static struct sdio_driver sdio_driver = {
.name = "cw1200_wlan",
- .id_table = if_sdio_ids,
+ .id_table = cw1200_sdio_ids,
.probe = cw1200_sdio_probe,
.remove = cw1200_sdio_disconnect,
};
diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c
index 15dd4fd5a1b..8a621ff2b06 100644
--- a/drivers/staging/cw1200/main.c
+++ b/drivers/staging/cw1200/main.c
@@ -26,7 +26,6 @@
#include <linux/vmalloc.h>
#include <linux/random.h>
#include <linux/sched.h>
-
#include <net/mac80211.h>
#include "cw1200.h"
@@ -39,6 +38,7 @@
#include "ap.h"
#include "scan.h"
#include "debug.h"
+#include "pm.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
@@ -220,6 +220,10 @@ static const struct ieee80211_ops cw1200_ops = {
.get_stats = cw1200_get_stats,
.ampdu_action = cw1200_ampdu_action,
.flush = cw1200_flush,
+#ifdef CONFIG_PM
+ .suspend = cw1200_wow_suspend,
+ .resume = cw1200_wow_resume,
+#endif /* CONFIG_PM */
/* Intentionally not offloaded: */
/*.channel_switch = cw1200_channel_switch, */
/*.remain_on_channel = cw1200_remain_on_channel, */
@@ -412,16 +416,21 @@ void cw1200_unregister_common(struct ieee80211_hw *dev)
priv->skb_cache = NULL;
}
+ if (priv->sdd) {
+ release_firmware(priv->sdd);
+ priv->sdd = NULL;
+ }
+
for (i = 0; i < 4; ++i)
cw1200_queue_deinit(&priv->tx_queue[i]);
cw1200_queue_stats_deinit(&priv->tx_queue_stats);
}
EXPORT_SYMBOL_GPL(cw1200_unregister_common);
-int cw1200_probe(const struct sbus_ops *sbus_ops,
- struct sbus_priv *sbus,
- struct device *pdev,
- struct cw1200_common **pself)
+int cw1200_core_probe(const struct sbus_ops *sbus_ops,
+ struct sbus_priv *sbus,
+ struct device *pdev,
+ struct cw1200_common **pself)
{
int err = -ENOMEM;
struct ieee80211_hw *dev;
@@ -489,12 +498,12 @@ err1:
err:
return err;
}
-EXPORT_SYMBOL_GPL(cw1200_probe);
+EXPORT_SYMBOL_GPL(cw1200_core_probe);
-void cw1200_release(struct cw1200_common *self)
+void cw1200_core_release(struct cw1200_common *self)
{
cw1200_unregister_common(self->hw);
cw1200_free_common(self->hw);
return;
}
-EXPORT_SYMBOL_GPL(cw1200_release);
+EXPORT_SYMBOL_GPL(cw1200_core_release);
diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c
new file mode 100644
index 00000000000..72f11e09d50
--- /dev/null
+++ b/drivers/staging/cw1200/pm.c
@@ -0,0 +1,142 @@
+/*
+ * Mac80211 power management API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "cw1200.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "sbus.h"
+
+/* private */
+struct cw1200_suspend_state {
+ unsigned long bss_loss_tmo;
+ unsigned long connection_loss_tmo;
+ unsigned long join_tmo;
+};
+
+static long cw1200_suspend_work(struct delayed_work *work)
+{
+ int ret = cancel_delayed_work(work);
+ long tmo;
+ if (ret > 0) {
+ /* Timer is pending */
+ tmo = work->timer.expires - jiffies;
+ if (tmo < 0)
+ tmo = 0;
+ } else {
+ tmo = -1;
+ }
+ return tmo;
+}
+
+static int cw1200_resume_work(struct cw1200_common *priv,
+ struct delayed_work *work,
+ unsigned long tmo)
+{
+ if ((long)tmo < 0)
+ return 1;
+
+ return queue_delayed_work(priv->workqueue, work, tmo);
+}
+
+int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_suspend_state *state;
+ int ret;
+
+ /* Ensure pending operations are done. */
+ ret = wait_event_interruptible_timeout(
+ priv->channel_switch_done,
+ !priv->channel_switch_in_progress, 3 * HZ);
+ if (WARN_ON(!ret))
+ return -ETIMEDOUT;
+ else if (WARN_ON(ret < 0))
+ return ret;
+
+ /* Flush and lock TX. */
+ ret = __cw1200_flush(priv, false);
+ if (WARN_ON(ret < 0))
+ return ret;
+
+ /* Allocate state */
+ state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
+ if (!state) {
+ wsm_unlock_tx(priv);
+ return -ENOMEM;
+ }
+
+ /* Store delayed work states. */
+ state->bss_loss_tmo =
+ cw1200_suspend_work(&priv->bss_loss_work);
+ state->connection_loss_tmo =
+ cw1200_suspend_work(&priv->connection_loss_work);
+ state->join_tmo =
+ cw1200_suspend_work(&priv->join_timeout);
+
+ /* Flush workqueue */
+ flush_workqueue(priv->workqueue);
+
+ /* Stop serving thread */
+ cw1200_bh_suspend(priv);
+
+ /* Store suspend state */
+ priv->suspend_state = state;
+
+ /* Enable IRQ wake */
+ ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true);
+ if (ret) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: PM request failed: %d. WoW is disabled.\n",
+ __func__, ret);
+ cw1200_wow_resume(hw);
+ return -EBUSY;
+ }
+
+ /* Force resume if event is coming from the device. */
+ if (atomic_read(&priv->bh_rx)) {
+ cw1200_wow_resume(hw);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+int cw1200_wow_resume(struct ieee80211_hw *hw)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_suspend_state *state;
+
+ state = priv->suspend_state;
+ priv->suspend_state = NULL;
+
+ /* Disable IRQ wake */
+ priv->sbus_ops->power_mgmt(priv->sbus_priv, false);
+
+ /* Resume BH thread */
+ cw1200_bh_resume(priv);
+
+ /* Resume delayed work */
+ cw1200_resume_work(priv, &priv->bss_loss_work,
+ state->bss_loss_tmo);
+ cw1200_resume_work(priv, &priv->connection_loss_work,
+ state->connection_loss_tmo);
+ cw1200_resume_work(priv, &priv->join_timeout,
+ state->join_tmo);
+
+ /* Unlock datapath */
+ wsm_unlock_tx(priv);
+
+ /* Free memory */
+ kfree(state);
+
+ return 0;
+}
diff --git a/drivers/staging/cw1200/pm.h b/drivers/staging/cw1200/pm.h
new file mode 100644
index 00000000000..841b609457f
--- /dev/null
+++ b/drivers/staging/cw1200/pm.h
@@ -0,0 +1,23 @@
+/*
+ * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+#ifdef CONFIG_PM
+int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+int cw1200_wow_resume(struct ieee80211_hw *hw);
+#endif /* CONFIG_PM */
+
+#endif
diff --git a/drivers/staging/cw1200/sbus.h b/drivers/staging/cw1200/sbus.h
index c31a2f30a22..a911ef1a0e6 100644
--- a/drivers/staging/cw1200/sbus.h
+++ b/drivers/staging/cw1200/sbus.h
@@ -32,6 +32,7 @@ struct sbus_ops {
int (*irq_unsubscribe)(struct sbus_priv *self);
int (*reset)(struct sbus_priv *self);
size_t (*align_size)(struct sbus_priv *self, size_t size);
+ int (*power_mgmt)(struct sbus_priv *self, bool suspend);
};
#endif /* CW1200_SBUS_H */
diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c
index 50f447fb46b..fe2cd5cc982 100644
--- a/drivers/staging/cw1200/sta.c
+++ b/drivers/staging/cw1200/sta.c
@@ -27,7 +27,6 @@
static int cw1200_cancel_scan(struct cw1200_common *priv);
-static int __cw1200_flush(struct cw1200_common *priv, bool drop);
static inline void __cw1200_free_event_queue(struct list_head *list)
{
@@ -652,7 +651,7 @@ int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
return ret;
}
-static int __cw1200_flush(struct cw1200_common *priv, bool drop)
+int __cw1200_flush(struct cw1200_common *priv, bool drop)
{
int i, ret;
@@ -859,43 +858,42 @@ int cw1200_setup_mac(struct cw1200_common *priv)
if (wsm_get_station_id(priv, &prev_mac[0])
|| memcmp(prev_mac, priv->mac_addr, ETH_ALEN)) {
const char *sdd_path = NULL;
- const struct firmware *firmware = NULL;
struct wsm_configuration cfg = {
.dot11StationId = &priv->mac_addr[0],
};
- switch (priv->hw_revision) {
- case CW1200_HW_REV_CUT10:
- sdd_path = SDD_FILE_10;
- break;
- case CW1200_HW_REV_CUT11:
- sdd_path = SDD_FILE_11;
- break;
- case CW1200_HW_REV_CUT20:
- sdd_path = SDD_FILE_20;
- break;
- case CW1200_HW_REV_CUT22:
- sdd_path = SDD_FILE_22;
- break;
- default:
- BUG_ON(1);
- }
+ if (!priv->sdd) {
+ switch (priv->hw_revision) {
+ case CW1200_HW_REV_CUT10:
+ sdd_path = SDD_FILE_10;
+ break;
+ case CW1200_HW_REV_CUT11:
+ sdd_path = SDD_FILE_11;
+ break;
+ case CW1200_HW_REV_CUT20:
+ sdd_path = SDD_FILE_20;
+ break;
+ case CW1200_HW_REV_CUT22:
+ sdd_path = SDD_FILE_22;
+ break;
+ default:
+ BUG_ON(1);
+ }
- ret = request_firmware(&firmware,
- sdd_path, priv->pdev);
+ ret = request_firmware(&priv->sdd,
+ sdd_path, priv->pdev);
- if (unlikely(ret)) {
- cw1200_dbg(CW1200_DBG_ERROR,
- "%s: can't load sdd file %s.\n",
- __func__, sdd_path);
- return ret;
+ if (unlikely(ret)) {
+ cw1200_dbg(CW1200_DBG_ERROR,
+ "%s: can't load sdd file %s.\n",
+ __func__, sdd_path);
+ return ret;
+ }
}
- cfg.dpdData = firmware->data;
- cfg.dpdData_size = firmware->size;
+ cfg.dpdData = priv->sdd->data;
+ cfg.dpdData_size = priv->sdd->size;
ret = WARN_ON(wsm_configuration(priv, &cfg));
-
- release_firmware(firmware);
}
if (ret)
return ret;
diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h
index 8951a4a176b..8d0cc5d4c12 100644
--- a/drivers/staging/cw1200/sta.h
+++ b/drivers/staging/cw1200/sta.h
@@ -72,5 +72,6 @@ void cw1200_unjoin_work(struct work_struct *work);
void cw1200_wep_key_work(struct work_struct *work);
void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
void cw1200_update_filtering(struct cw1200_common *priv);
+int __cw1200_flush(struct cw1200_common *priv, bool drop);
#endif
diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c
index e8662fa6054..46280046bae 100644
--- a/drivers/staging/cw1200/wsm.c
+++ b/drivers/staging/cw1200/wsm.c
@@ -1060,7 +1060,7 @@ void wsm_lock_tx(struct cw1200_common *priv)
{
wsm_cmd_lock(priv);
if (atomic_add_return(1, &priv->tx_lock) == 1) {
- WARN_ON(wait_event_interruptible_timeout(priv->hw_bufs_used_wq,
+ WARN_ON(wait_event_interruptible_timeout(priv->bh_evt_wq,
!priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n");
}
@@ -1076,7 +1076,7 @@ void wsm_lock_tx_async(struct cw1200_common *priv)
void wsm_flush_tx(struct cw1200_common *priv)
{
BUG_ON(!atomic_read(&priv->tx_lock));
- WARN_ON(wait_event_interruptible_timeout(priv->hw_bufs_used_wq,
+ WARN_ON(wait_event_interruptible_timeout(priv->bh_evt_wq,
!priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
}