[PATCH v3] libertas: Added callback functions to support SDIO suspend/resume.
Dan Williams
dcbw at redhat.com
Tue May 18 00:32:02 EDT 2010
On Fri, 2010-05-14 at 02:32 -0700, Amitkumar Karwar wrote:
> In suspend() host sleep is activated using already configured
> host sleep parameters through wol command, and in resume() host
> sleep is cancelled. Earlier priv->fw_ready flag used to reset and
> set in suspend and resume handler respectively. Since after suspend
> only host goes into sleep state and firmware is always ready, those
> changes in flag state are removed.
>
> Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
> Signed-off-by: Kiran Divekar <dkiran at marvell.com>
Looks OK to me...
Acked-by: Dan Williams <dcbw at redhat.com>
> ---
> drivers/net/wireless/libertas/cmd.c | 37 ++++++++++++--
> drivers/net/wireless/libertas/cmdresp.c | 30 ++---------
> drivers/net/wireless/libertas/decl.h | 2 +-
> drivers/net/wireless/libertas/dev.h | 6 ++
> drivers/net/wireless/libertas/ethtool.c | 15 +++---
> drivers/net/wireless/libertas/if_sdio.c | 58 ++++++++++++++++++++++
> drivers/net/wireless/libertas/if_usb.c | 6 ++
> drivers/net/wireless/libertas/main.c | 79 +++++++++++++++++++++---------
> 8 files changed, 171 insertions(+), 62 deletions(-)
>
> diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
> index cdb9b96..0fa6b0e 100644
> --- a/drivers/net/wireless/libertas/cmd.c
> +++ b/drivers/net/wireless/libertas/cmd.c
> @@ -70,6 +70,8 @@ static u8 is_command_allowed_in_ps(u16 cmd)
> switch (cmd) {
> case CMD_802_11_RSSI:
> return 1;
> + case CMD_802_11_HOST_SLEEP_CFG:
> + return 1;
> default:
> break;
> }
> @@ -185,6 +187,23 @@ out:
> return ret;
> }
>
> +static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
> + struct cmd_header *resp)
> +{
> + lbs_deb_enter(LBS_DEB_CMD);
> + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
> + priv->is_host_sleep_configured = 0;
> + if (priv->psstate == PS_STATE_FULL_POWER) {
> + priv->is_host_sleep_activated = 0;
> + wake_up_interruptible(&priv->host_sleep_q);
> + }
> + } else {
> + priv->is_host_sleep_configured = 1;
> + }
> + lbs_deb_leave(LBS_DEB_CMD);
> + return 0;
> +}
> +
> int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
> struct wol_config *p_wol_config)
> {
> @@ -202,12 +221,11 @@ int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
> else
> cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
>
> - ret = lbs_cmd_with_response(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config);
> + ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr,
> + le16_to_cpu(cmd_config.hdr.size),
> + lbs_ret_host_sleep_cfg, 0);
> if (!ret) {
> - if (criteria) {
> - lbs_deb_cmd("Set WOL criteria to %x\n", criteria);
> - priv->wol_criteria = criteria;
> - } else
> + if (p_wol_config)
> memcpy((uint8_t *) p_wol_config,
> (uint8_t *)&cmd_config.wol_conf,
> sizeof(struct wol_config));
> @@ -712,6 +730,10 @@ static void lbs_queue_cmd(struct lbs_private *priv,
> }
> }
>
> + if (le16_to_cpu(cmdnode->cmdbuf->command) ==
> + CMD_802_11_WAKEUP_CONFIRM)
> + addtail = 0;
> +
> spin_lock_irqsave(&priv->driver_lock, flags);
>
> if (addtail)
> @@ -1353,6 +1375,11 @@ static void lbs_send_confirmsleep(struct lbs_private *priv)
> /* We don't get a response on the sleep-confirmation */
> priv->dnld_sent = DNLD_RES_RECEIVED;
>
> + if (priv->is_host_sleep_configured) {
> + priv->is_host_sleep_activated = 1;
> + wake_up_interruptible(&priv->host_sleep_q);
> + }
> +
> /* If nothing to do, go back to sleep (?) */
> if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
> priv->psstate = PS_STATE_SLEEP;
> diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
> index 88f7131..d6c3063 100644
> --- a/drivers/net/wireless/libertas/cmdresp.c
> +++ b/drivers/net/wireless/libertas/cmdresp.c
> @@ -17,6 +17,7 @@
> #include "dev.h"
> #include "assoc.h"
> #include "wext.h"
> +#include "cmd.h"
>
> /**
> * @brief This function handles disconnect event. it
> @@ -341,32 +342,10 @@ done:
> return ret;
> }
>
> -static int lbs_send_confirmwake(struct lbs_private *priv)
> -{
> - struct cmd_header cmd;
> - int ret = 0;
> -
> - lbs_deb_enter(LBS_DEB_HOST);
> -
> - cmd.command = cpu_to_le16(CMD_802_11_WAKEUP_CONFIRM);
> - cmd.size = cpu_to_le16(sizeof(cmd));
> - cmd.seqnum = cpu_to_le16(++priv->seqnum);
> - cmd.result = 0;
> -
> - lbs_deb_hex(LBS_DEB_HOST, "wake confirm", (u8 *) &cmd,
> - sizeof(cmd));
> -
> - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &cmd, sizeof(cmd));
> - if (ret)
> - lbs_pr_alert("SEND_WAKEC_CMD: Host to Card failed for Confirm Wake\n");
> -
> - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
> - return ret;
> -}
> -
> int lbs_process_event(struct lbs_private *priv, u32 event)
> {
> int ret = 0;
> + struct cmd_header cmd;
>
> lbs_deb_enter(LBS_DEB_CMD);
>
> @@ -410,7 +389,10 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
> if (priv->reset_deep_sleep_wakeup)
> priv->reset_deep_sleep_wakeup(priv);
> priv->is_deep_sleep = 0;
> - lbs_send_confirmwake(priv);
> + lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd,
> + sizeof(cmd));
> + priv->is_host_sleep_activated = 0;
> + wake_up_interruptible(&priv->host_sleep_q);
> break;
>
> case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
> diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
> index 709ffca..61db8bc 100644
> --- a/drivers/net/wireless/libertas/decl.h
> +++ b/drivers/net/wireless/libertas/decl.h
> @@ -38,7 +38,7 @@ int lbs_set_mac_address(struct net_device *dev, void *addr);
> void lbs_set_multicast_list(struct net_device *dev);
>
> int lbs_suspend(struct lbs_private *priv);
> -void lbs_resume(struct lbs_private *priv);
> +int lbs_resume(struct lbs_private *priv);
>
> void lbs_queue_event(struct lbs_private *priv, u32 event);
> void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
> diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> index a54880e..71c5ad4 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -75,6 +75,7 @@ struct lbs_private {
>
> /* Deep sleep */
> int is_deep_sleep;
> + int deep_sleep_required;
> int is_auto_deep_sleep_enabled;
> int wakeup_dev_required;
> int is_activity_detected;
> @@ -82,6 +83,11 @@ struct lbs_private {
> wait_queue_head_t ds_awake_q;
> struct timer_list auto_deepsleep_timer;
>
> + /* Host sleep*/
> + int is_host_sleep_configured;
> + int is_host_sleep_activated;
> + wait_queue_head_t host_sleep_q;
> +
> /* Hardware access */
> void *card;
> u8 fw_ready;
> diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
> index 3804a58..6a36c99 100644
> --- a/drivers/net/wireless/libertas/ethtool.c
> +++ b/drivers/net/wireless/libertas/ethtool.c
> @@ -91,23 +91,22 @@ static int lbs_ethtool_set_wol(struct net_device *dev,
> struct ethtool_wolinfo *wol)
> {
> struct lbs_private *priv = dev->ml_priv;
> - uint32_t criteria = 0;
>
> if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY))
> return -EOPNOTSUPP;
>
> + priv->wol_criteria = 0;
> if (wol->wolopts & WAKE_UCAST)
> - criteria |= EHS_WAKE_ON_UNICAST_DATA;
> + priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA;
> if (wol->wolopts & WAKE_MCAST)
> - criteria |= EHS_WAKE_ON_MULTICAST_DATA;
> + priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA;
> if (wol->wolopts & WAKE_BCAST)
> - criteria |= EHS_WAKE_ON_BROADCAST_DATA;
> + priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA;
> if (wol->wolopts & WAKE_PHY)
> - criteria |= EHS_WAKE_ON_MAC_EVENT;
> + priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT;
> if (wol->wolopts == 0)
> - criteria |= EHS_REMOVE_WAKEUP;
> -
> - return lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL);
> + priv->wol_criteria |= EHS_REMOVE_WAKEUP;
> + return 0;
> }
>
> const struct ethtool_ops lbs_ethtool_ops = {
> diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
> index 64dd345..575d433 100644
> --- a/drivers/net/wireless/libertas/if_sdio.c
> +++ b/drivers/net/wireless/libertas/if_sdio.c
> @@ -1182,11 +1182,69 @@ static void if_sdio_remove(struct sdio_func *func)
> lbs_deb_leave(LBS_DEB_SDIO);
> }
>
> +static int if_sdio_suspend(struct device *dev)
> +{
> + struct sdio_func *func = dev_to_sdio_func(dev);
> + int ret;
> + struct if_sdio_card *card = sdio_get_drvdata(func);
> +
> + mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
> +
> + lbs_pr_info("%s: suspend: PM flags = 0x%x\n",
> + sdio_func_id(func), flags);
> +
> + /* If we aren't being asked to wake on anything, we should bail out
> + * and let the SD stack power down the card.
> + */
> + if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) {
> + lbs_pr_info("Suspend without wake params -- "
> + "powering down card.");
> + return -ENOSYS;
> + }
> +
> + if (!(flags & MMC_PM_KEEP_POWER)) {
> + lbs_pr_err("%s: cannot remain alive while host is suspended\n",
> + sdio_func_id(func));
> + return -ENOSYS;
> + }
> +
> + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
> + if (ret)
> + return ret;
> +
> + ret = lbs_suspend(card->priv);
> + if (ret)
> + return ret;
> +
> + return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
> +}
> +
> +static int if_sdio_resume(struct device *dev)
> +{
> + struct sdio_func *func = dev_to_sdio_func(dev);
> + struct if_sdio_card *card = sdio_get_drvdata(func);
> + int ret;
> +
> + lbs_pr_info("%s: resume: we're back\n", sdio_func_id(func));
> +
> + ret = lbs_resume(card->priv);
> +
> + return ret;
> +}
> +
> +static struct const dev_pm_ops if_sdio_pm_ops = {
> + .suspend = if_sdio_suspend,
> + .resume = if_sdio_resume,
> +};
> +
> static struct sdio_driver if_sdio_driver = {
> .name = "libertas_sdio",
> .id_table = if_sdio_ids,
> .probe = if_sdio_probe,
> .remove = if_sdio_remove,
> + .drv = {
> + .pm = &if_sdio_pm_ops,
> + },
> };
>
> /*******************************************************************/
> diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
> index fcea574..e5e6801 100644
> --- a/drivers/net/wireless/libertas/if_usb.c
> +++ b/drivers/net/wireless/libertas/if_usb.c
> @@ -1047,6 +1047,12 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
> if (priv->psstate != PS_STATE_FULL_POWER)
> return -1;
>
> + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
> + lbs_pr_info("Suspend attempt without "
> + "configuring wake params!\n");
> + return -ENOSYS;
> + }
> +
> ret = lbs_suspend(priv);
> if (ret)
> goto out;
> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> index d35ebca..25c5734 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -625,16 +625,13 @@ static int lbs_thread(void *data)
> return 0;
> }
>
> -static int lbs_suspend_callback(struct lbs_private *priv, unsigned long dummy,
> - struct cmd_header *cmd)
> +static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
> + unsigned long dummy,
> + struct cmd_header *cmd)
> {
> lbs_deb_enter(LBS_DEB_FW);
> -
> - netif_device_detach(priv->dev);
> - if (priv->mesh_dev)
> - netif_device_detach(priv->mesh_dev);
> -
> - priv->fw_ready = 0;
> + priv->is_host_sleep_activated = 1;
> + wake_up_interruptible(&priv->host_sleep_q);
> lbs_deb_leave(LBS_DEB_FW);
> return 0;
> }
> @@ -646,39 +643,65 @@ int lbs_suspend(struct lbs_private *priv)
>
> lbs_deb_enter(LBS_DEB_FW);
>
> - if (priv->wol_criteria == 0xffffffff) {
> - lbs_pr_info("Suspend attempt without configuring wake params!\n");
> - return -EINVAL;
> + if (priv->is_deep_sleep) {
> + ret = lbs_set_deep_sleep(priv, 0);
> + if (ret) {
> + lbs_pr_err("deep sleep cancellation failed: %d\n", ret);
> + return ret;
> + }
> + priv->deep_sleep_required = 1;
> }
>
> memset(&cmd, 0, sizeof(cmd));
> + ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
> + (struct wol_config *)NULL);
> + if (ret) {
> + lbs_pr_info("Host sleep configuration failed: %d\n", ret);
> + return ret;
> + }
> + if (priv->psstate == PS_STATE_FULL_POWER) {
> + ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd,
> + sizeof(cmd), lbs_ret_host_sleep_activate, 0);
> + if (ret)
> + lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret);
> + }
>
> - ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd,
> - sizeof(cmd), lbs_suspend_callback, 0);
> - if (ret)
> - lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret);
> + if (!wait_event_interruptible_timeout(priv->host_sleep_q,
> + priv->is_host_sleep_activated, (10 * HZ))) {
> + lbs_pr_err("host_sleep_q: timer expired\n");
> + ret = -1;
> + }
> + netif_device_detach(priv->dev);
> + if (priv->mesh_dev)
> + netif_device_detach(priv->mesh_dev);
>
> lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
> return ret;
> }
> EXPORT_SYMBOL_GPL(lbs_suspend);
>
> -void lbs_resume(struct lbs_private *priv)
> +int lbs_resume(struct lbs_private *priv)
> {
> - lbs_deb_enter(LBS_DEB_FW);
> + int ret;
> + uint32_t criteria = EHS_REMOVE_WAKEUP;
>
> - priv->fw_ready = 1;
> + lbs_deb_enter(LBS_DEB_FW);
>
> - /* Firmware doesn't seem to give us RX packets any more
> - until we send it some command. Might as well update */
> - lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
> - 0, 0, NULL);
> + ret = lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL);
>
> netif_device_attach(priv->dev);
> if (priv->mesh_dev)
> netif_device_attach(priv->mesh_dev);
>
> - lbs_deb_leave(LBS_DEB_FW);
> + if (priv->deep_sleep_required) {
> + priv->deep_sleep_required = 0;
> + ret = lbs_set_deep_sleep(priv, 1);
> + if (ret)
> + lbs_pr_err("deep sleep activation failed: %d\n", ret);
> + }
> +
> + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
> + return ret;
> }
> EXPORT_SYMBOL_GPL(lbs_resume);
>
> @@ -834,10 +857,13 @@ static int lbs_init_adapter(struct lbs_private *priv)
> priv->psstate = PS_STATE_FULL_POWER;
> priv->is_deep_sleep = 0;
> priv->is_auto_deep_sleep_enabled = 0;
> + priv->deep_sleep_required = 0;
> priv->wakeup_dev_required = 0;
> init_waitqueue_head(&priv->ds_awake_q);
> priv->authtype_auto = 1;
> -
> + priv->is_host_sleep_configured = 0;
> + priv->is_host_sleep_activated = 0;
> + init_waitqueue_head(&priv->host_sleep_q);
> mutex_init(&priv->lock);
>
> setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
> @@ -976,6 +1002,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
>
> priv->wol_criteria = 0xffffffff;
> priv->wol_gpio = 0xff;
> + priv->wol_gap = 20;
>
> goto done;
>
> @@ -1031,6 +1058,10 @@ void lbs_remove_card(struct lbs_private *priv)
> wake_up_interruptible(&priv->ds_awake_q);
> }
>
> + priv->is_host_sleep_configured = 0;
> + priv->is_host_sleep_activated = 0;
> + wake_up_interruptible(&priv->host_sleep_q);
> +
> /* Stop the thread servicing the interrupts */
> priv->surpriseremoved = 1;
> kthread_stop(priv->main_thread);
More information about the libertas-dev
mailing list