Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / net / wireless / libertas / if_sdio.c
index e998c12..485a8d4 100644 (file)
 #include "decl.h"
 #include "defs.h"
 #include "dev.h"
+#include "cmd.h"
 #include "if_sdio.h"
 
+/* The if_sdio_remove() callback function is called when
+ * user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently for SD8688 combo chip.
+ * If the user is removing the module, the FUNC_SHUTDOWN
+ * command for SD8688 is sent to the firmware.
+ * If the card is removed, there is no need to send this command.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+
 static char *lbs_helper_name = NULL;
 module_param_named(helper_name, lbs_helper_name, charp, 0644);
 
@@ -96,6 +112,7 @@ struct if_sdio_card {
 
        int                     model;
        unsigned long           ioport;
+       unsigned int            scratch_reg;
 
        const char              *helper;
        const char              *firmware;
@@ -115,19 +132,21 @@ struct if_sdio_card {
 /* I/O                                                              */
 /********************************************************************/
 
+/*
+ *  For SD8385/SD8686, this function reads firmware status after
+ *  the image is downloaded, or reads RX packet length when
+ *  interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
+ *  For SD8688, this function reads firmware status only.
+ */
 static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
 {
-       int ret, reg;
+       int ret;
        u16 scratch;
 
-       if (card->model == IF_SDIO_MODEL_8385)
-               reg = IF_SDIO_SCRATCH_OLD;
-       else
-               reg = IF_SDIO_SCRATCH;
-
-       scratch = sdio_readb(card->func, reg, &ret);
+       scratch = sdio_readb(card->func, card->scratch_reg, &ret);
        if (!ret)
-               scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;
+               scratch |= sdio_readb(card->func, card->scratch_reg + 1,
+                                       &ret) << 8;
 
        if (err)
                *err = ret;
@@ -539,7 +558,6 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
        ret = 0;
 
 release:
-       sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
        sdio_release_host(card->func);
        kfree(chunk_buffer);
 release_fw:
@@ -675,7 +693,6 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
        ret = 0;
 
 release:
-       sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
        sdio_release_host(card->func);
        kfree(chunk_buffer);
 release_fw:
@@ -704,6 +721,8 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
        if (ret)
                goto out;
 
+       lbs_deb_sdio("firmware status = %#x\n", scratch);
+
        if (scratch == IF_SDIO_FIRMWARE_OK) {
                lbs_deb_sdio("firmware already loaded\n");
                goto success;
@@ -718,6 +737,9 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
                goto out;
 
 success:
+       sdio_claim_host(card->func);
+       sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
+       sdio_release_host(card->func);
        ret = 0;
 
 out:
@@ -888,6 +910,20 @@ static int if_sdio_probe(struct sdio_func *func,
 
        card->func = func;
        card->model = model;
+
+       switch (card->model) {
+       case IF_SDIO_MODEL_8385:
+               card->scratch_reg = IF_SDIO_SCRATCH_OLD;
+               break;
+       case IF_SDIO_MODEL_8686:
+               card->scratch_reg = IF_SDIO_SCRATCH;
+               break;
+       case IF_SDIO_MODEL_8688:
+       default: /* for newer chipsets */
+               card->scratch_reg = IF_SDIO_FW_STATUS;
+               break;
+       }
+
        spin_lock_init(&card->lock);
        card->workqueue = create_workqueue("libertas_sdio");
        INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
@@ -985,13 +1021,24 @@ static int if_sdio_probe(struct sdio_func *func,
        if (ret)
                goto reclaim;
 
+       /*
+        * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
+        */
+       if (card->model == IF_SDIO_MODEL_8688) {
+               struct cmd_header cmd;
+
+               memset(&cmd, 0, sizeof(cmd));
+
+               lbs_deb_sdio("send function INIT command\n");
+               if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
+                               lbs_cmd_copyback, (unsigned long) &cmd))
+                       lbs_pr_alert("CMD_FUNC_INIT cmd failed\n");
+       }
+
        ret = lbs_start_card(priv);
        if (ret)
                goto err_activate_card;
 
-       if (priv->fwcapinfo & FW_CAPINFO_PS)
-               priv->ps_supported = 1;
-
 out:
        lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
 
@@ -1030,11 +1077,27 @@ static void if_sdio_remove(struct sdio_func *func)
 
        card = sdio_get_drvdata(func);
 
-       card->priv->surpriseremoved = 1;
+       if (user_rmmod && (card->model == IF_SDIO_MODEL_8688)) {
+               /*
+                * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
+                * multiple functions
+                */
+               struct cmd_header cmd;
+
+               memset(&cmd, 0, sizeof(cmd));
+
+               lbs_deb_sdio("send function SHUTDOWN command\n");
+               if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN,
+                               &cmd, sizeof(cmd), lbs_cmd_copyback,
+                               (unsigned long) &cmd))
+                       lbs_pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n");
+       }
+
 
        lbs_deb_sdio("call remove card\n");
        lbs_stop_card(card->priv);
        lbs_remove_card(card->priv);
+       card->priv->surpriseremoved = 1;
 
        flush_workqueue(card->workqueue);
        destroy_workqueue(card->workqueue);
@@ -1077,6 +1140,9 @@ static int __init if_sdio_init_module(void)
 
        ret = sdio_register_driver(&if_sdio_driver);
 
+       /* Clear the flag in case user removes the card. */
+       user_rmmod = 0;
+
        lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
 
        return ret;
@@ -1086,6 +1152,9 @@ static void __exit if_sdio_exit_module(void)
 {
        lbs_deb_enter(LBS_DEB_SDIO);
 
+       /* Set the flag as user is removing this module. */
+       user_rmmod = 1;
+
        sdio_unregister_driver(&if_sdio_driver);
 
        lbs_deb_leave(LBS_DEB_SDIO);