libertas SDIO: convert to asynchronous firmware loading
authorDaniel Drake <dsd@laptop.org>
Mon, 16 Apr 2012 22:53:43 +0000 (23:53 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 17 Apr 2012 18:57:14 +0000 (14:57 -0400)
Signed-off-by: Daniel Drake <dsd@laptop.org>
Acked-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/libertas/if_sdio.c

index 6590febb366be96b5e29ec04414915eae1c40405..76caebaa43977f722cbd707d666f1f014002792e 100644 (file)
@@ -117,6 +117,8 @@ struct if_sdio_card {
        int                     model;
        unsigned long           ioport;
        unsigned int            scratch_reg;
+       bool                    started;
+       wait_queue_head_t       pwron_waitq;
 
        u8                      buffer[65536] __attribute__((aligned(4)));
 
@@ -129,6 +131,9 @@ struct if_sdio_card {
        u8                      rx_unit;
 };
 
+static void if_sdio_finish_power_on(struct if_sdio_card *card);
+static int if_sdio_power_off(struct if_sdio_card *card);
+
 /********************************************************************/
 /* I/O                                                              */
 /********************************************************************/
@@ -669,12 +674,39 @@ out:
        return ret;
 }
 
+static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret,
+                                    const struct firmware *helper,
+                                    const struct firmware *mainfw)
+{
+       struct if_sdio_card *card = priv->card;
+
+       if (ret) {
+               pr_err("failed to find firmware (%d)\n", ret);
+               return;
+       }
+
+       ret = if_sdio_prog_helper(card, helper);
+       if (ret)
+               goto out;
+
+       lbs_deb_sdio("Helper firmware loaded\n");
+
+       ret = if_sdio_prog_real(card, mainfw);
+       if (ret)
+               goto out;
+
+       lbs_deb_sdio("Firmware loaded\n");
+       if_sdio_finish_power_on(card);
+
+out:
+       release_firmware(helper);
+       release_firmware(mainfw);
+}
+
 static int if_sdio_prog_firmware(struct if_sdio_card *card)
 {
        int ret;
        u16 scratch;
-       const struct firmware *helper = NULL;
-       const struct firmware *mainfw = NULL;
 
        lbs_deb_enter(LBS_DEB_SDIO);
 
@@ -708,41 +740,18 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
         */
        if (scratch == IF_SDIO_FIRMWARE_OK) {
                lbs_deb_sdio("firmware already loaded\n");
-               goto success;
+               if_sdio_finish_power_on(card);
+               return 0;
        } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) {
                lbs_deb_sdio("firmware may be running\n");
-               goto success;
-       }
-
-       ret = lbs_get_firmware(&card->func->dev, card->model, &fw_table[0],
-                               &helper, &mainfw);
-       if (ret) {
-               pr_err("failed to find firmware (%d)\n", ret);
-               goto out;
+               if_sdio_finish_power_on(card);
+               return 0;
        }
 
-       ret = if_sdio_prog_helper(card, helper);
-       if (ret)
-               goto out;
-
-       lbs_deb_sdio("Helper firmware loaded\n");
-
-       ret = if_sdio_prog_real(card, mainfw);
-       if (ret)
-               goto out;
-
-       lbs_deb_sdio("Firmware loaded\n");
-
-success:
-       sdio_claim_host(card->func);
-       sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
-       sdio_release_host(card->func);
-       ret = 0;
+       ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model,
+                                    fw_table, if_sdio_do_prog_firmware);
 
 out:
-       release_firmware(helper);
-       release_firmware(mainfw);
-
        lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
        return ret;
 }
@@ -751,55 +760,15 @@ out:
 /* Power management                                                 */
 /********************************************************************/
 
-static int if_sdio_power_on(struct if_sdio_card *card)
+/* Finish power on sequence (after firmware is loaded) */
+static void if_sdio_finish_power_on(struct if_sdio_card *card)
 {
        struct sdio_func *func = card->func;
        struct lbs_private *priv = card->priv;
-       struct mmc_host *host = func->card->host;
        int ret;
 
        sdio_claim_host(func);
-
-       ret = sdio_enable_func(func);
-       if (ret)
-               goto release;
-
-       /* For 1-bit transfers to the 8686 model, we need to enable the
-        * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
-        * bit to allow access to non-vendor registers. */
-       if ((card->model == MODEL_8686) &&
-           (host->caps & MMC_CAP_SDIO_IRQ) &&
-           (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
-               u8 reg;
-
-               func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
-               reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
-               if (ret)
-                       goto disable;
-
-               reg |= SDIO_BUS_ECSI;
-               sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
-               if (ret)
-                       goto disable;
-       }
-
-       card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
-       if (ret)
-               goto disable;
-
-       card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
-       if (ret)
-               goto disable;
-
-       card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
-       if (ret)
-               goto disable;
-
-       sdio_release_host(func);
-       ret = if_sdio_prog_firmware(card);
-       sdio_claim_host(func);
-       if (ret)
-               goto disable;
+       sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
 
        /*
         * Get rx_unit if the chip is SD8688 or newer.
@@ -824,7 +793,7 @@ static int if_sdio_power_on(struct if_sdio_card *card)
         */
        ret = sdio_claim_irq(func, if_sdio_interrupt);
        if (ret)
-               goto disable;
+               goto release;
 
        /*
         * Enable interrupts now that everything is set up
@@ -850,11 +819,79 @@ static int if_sdio_power_on(struct if_sdio_card *card)
        }
 
        priv->fw_ready = 1;
+       wake_up(&card->pwron_waitq);
 
-       return 0;
+       if (!card->started) {
+               ret = lbs_start_card(priv);
+               if_sdio_power_off(card);
+               if (ret == 0) {
+                       card->started = true;
+                       /* Tell PM core that we don't need the card to be
+                        * powered now */
+                       pm_runtime_put_noidle(&func->dev);
+               }
+       }
+
+       return;
 
 release_irq:
        sdio_release_irq(func);
+release:
+       sdio_release_host(func);
+}
+
+static int if_sdio_power_on(struct if_sdio_card *card)
+{
+       struct sdio_func *func = card->func;
+       struct mmc_host *host = func->card->host;
+       int ret;
+
+       sdio_claim_host(func);
+
+       ret = sdio_enable_func(func);
+       if (ret)
+               goto release;
+
+       /* For 1-bit transfers to the 8686 model, we need to enable the
+        * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
+        * bit to allow access to non-vendor registers. */
+       if ((card->model == MODEL_8686) &&
+           (host->caps & MMC_CAP_SDIO_IRQ) &&
+           (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
+               u8 reg;
+
+               func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+               reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
+               if (ret)
+                       goto disable;
+
+               reg |= SDIO_BUS_ECSI;
+               sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
+               if (ret)
+                       goto disable;
+       }
+
+       card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
+       if (ret)
+               goto disable;
+
+       card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
+       if (ret)
+               goto disable;
+
+       card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
+       if (ret)
+               goto disable;
+
+       sdio_release_host(func);
+       ret = if_sdio_prog_firmware(card);
+       if (ret) {
+               sdio_disable_func(func);
+               return ret;
+       }
+
+       return 0;
+
 disable:
        sdio_disable_func(func);
 release:
@@ -1061,11 +1098,17 @@ static int if_sdio_power_save(struct lbs_private *priv)
 static int if_sdio_power_restore(struct lbs_private *priv)
 {
        struct if_sdio_card *card = priv->card;
+       int r;
 
        /* Make sure the card will not be powered off by runtime PM */
        pm_runtime_get_sync(&card->func->dev);
 
-       return if_sdio_power_on(card);
+       r = if_sdio_power_on(card);
+       if (r)
+               return r;
+
+       wait_event(card->pwron_waitq, priv->fw_ready);
+       return 0;
 }
 
 
@@ -1166,6 +1209,7 @@ static int if_sdio_probe(struct sdio_func *func,
        spin_lock_init(&card->lock);
        card->workqueue = create_workqueue("libertas_sdio");
        INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
+       init_waitqueue_head(&card->pwron_waitq);
 
        /* Check if we support this card */
        for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
@@ -1207,14 +1251,6 @@ static int if_sdio_probe(struct sdio_func *func,
        if (ret)
                goto err_activate_card;
 
-       ret = lbs_start_card(priv);
-       if_sdio_power_off(card);
-       if (ret)
-               goto err_activate_card;
-
-       /* Tell PM core that we don't need the card to be powered now */
-       pm_runtime_put_noidle(&func->dev);
-
 out:
        lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);