[RFC 4/4] libertas SDIO: convert to asynchronous firmware loading
Dan Williams
dcbw at redhat.com
Mon Apr 16 17:44:39 EDT 2012
On Tue, 2012-04-03 at 17:03 +0100, Daniel Drake wrote:
> Signed-off-by: Daniel Drake <dsd at laptop.org>
Works on my standalone SD8686 dev board; though I do get:
[32350.237619] libertas_sdio mmc0:0001:1: (unregistered net_device):
00:50:43:20:74:05, fw 9.70.3p36, cap 0x00000303
[32350.239847] libertas_sdio mmc0:0001:1: (unregistered net_device):
PREP_CMD: command 0x00a3 failed: 2
where the "unregistered net_device" is a bit odd. Perhaps we should fix
that before the final series.
Dan
> ---
> drivers/net/wireless/libertas/if_sdio.c | 210 ++++++++++++++++++-------------
> 1 files changed, 123 insertions(+), 87 deletions(-)
>
> diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
> index 26c7235..0e0fb2b 100644
> --- a/drivers/net/wireless/libertas/if_sdio.c
> +++ b/drivers/net/wireless/libertas/if_sdio.c
> @@ -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,41 @@ 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:
> + if (helper)
> + release_firmware(helper);
> + if (mainfw)
> + 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,43 +742,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;
> + if_sdio_finish_power_on(card);
> + return 0;
> }
>
> - 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;
> - }
> -
> - 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:
> - if (helper)
> - release_firmware(helper);
> - if (mainfw)
> - release_firmware(mainfw);
> -
> lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
> return ret;
> }
> @@ -753,55 +762,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.
> @@ -826,7 +795,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
> @@ -852,11 +821,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:
> @@ -1063,11 +1100,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;
> }
>
>
> @@ -1168,6 +1211,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++) {
> @@ -1209,14 +1253,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);
>
More information about the libertas-dev
mailing list