libertas: Added callback functions to support SDIO suspend/resume.
authorAmitkumar Karwar <akarwar@marvell.com>
Wed, 19 May 2010 10:24:38 +0000 (03:24 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 2 Jun 2010 20:13:06 +0000 (16:13 -0400)
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@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/cmdresp.c
drivers/net/wireless/libertas/decl.h
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/ethtool.c
drivers/net/wireless/libertas/if_sdio.c
drivers/net/wireless/libertas/if_usb.c
drivers/net/wireless/libertas/main.c

index cdb9b9650d73aaf1de0114966e398defa4cae8d1..0fa6b0e59ea58238e2a8cc91e257462d6d1fc587 100644 (file)
@@ -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;
index 88f7131d66e9cba7e62f6a46076a2878a3c05716..d6c3063536403a66119d4f4ced1d4c336f11b8e4 100644 (file)
@@ -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:
index 709ffcad22ad68953104b0f5fbfd8b4f6c5e5f4b..61db8bc62b3c2d519d1859ee01027cd1e942f258 100644 (file)
@@ -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);
index a54880e4ad2b99e00eeb0c3114bd796adafbd53b..71c5ad46ebf6a5ca8627f52bb31a92e62811feb0 100644 (file)
@@ -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;
index 3804a58d7f4e8903cb8788457c8b99f8bd469419..6a36c9956fdca32a5ad437d92441ed4efc239bca 100644 (file)
@@ -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 = {
index 64dd345d30f526f2c10ff0b4c0b03998798e0fc0..6e71346a75505a4398964a2889eef4803822f5a4 100644 (file)
@@ -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 const struct 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,
+       },
 };
 
 /*******************************************************************/
index f41594c7ac16a09e712cc75a3cf85323870b5c17..a0cb265e5816bacb6e91a3c3c90591dcdef4a90b 100644 (file)
@@ -1043,6 +1043,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;
index d9b8ee130c450eb4006cafa411baa7a5b5038f60..abfecc4814b47ac5ba8d9088a907857f85d574fd 100644 (file)
@@ -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);