Commit 66d647ef authored by Daniel Drake's avatar Daniel Drake Committed by John W. Linville

libertas SDIO: convert to asynchronous firmware loading

Signed-off-by: default avatarDaniel Drake <dsd@laptop.org>
Acked-by: default avatarDan Williams <dcbw@redhat.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 534111c7
...@@ -117,6 +117,8 @@ struct if_sdio_card { ...@@ -117,6 +117,8 @@ struct if_sdio_card {
int model; int model;
unsigned long ioport; unsigned long ioport;
unsigned int scratch_reg; unsigned int scratch_reg;
bool started;
wait_queue_head_t pwron_waitq;
u8 buffer[65536] __attribute__((aligned(4))); u8 buffer[65536] __attribute__((aligned(4)));
...@@ -129,6 +131,9 @@ struct if_sdio_card { ...@@ -129,6 +131,9 @@ struct if_sdio_card {
u8 rx_unit; 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 */ /* I/O */
/********************************************************************/ /********************************************************************/
...@@ -669,12 +674,39 @@ static int if_sdio_prog_real(struct if_sdio_card *card, ...@@ -669,12 +674,39 @@ static int if_sdio_prog_real(struct if_sdio_card *card,
return ret; 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) static int if_sdio_prog_firmware(struct if_sdio_card *card)
{ {
int ret; int ret;
u16 scratch; u16 scratch;
const struct firmware *helper = NULL;
const struct firmware *mainfw = NULL;
lbs_deb_enter(LBS_DEB_SDIO); lbs_deb_enter(LBS_DEB_SDIO);
...@@ -708,41 +740,18 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card) ...@@ -708,41 +740,18 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
*/ */
if (scratch == IF_SDIO_FIRMWARE_OK) { if (scratch == IF_SDIO_FIRMWARE_OK) {
lbs_deb_sdio("firmware already loaded\n"); 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)) { } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) {
lbs_deb_sdio("firmware may be running\n"); 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); ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model,
if (ret) fw_table, if_sdio_do_prog_firmware);
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;
out: out:
release_firmware(helper);
release_firmware(mainfw);
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
return ret; return ret;
} }
...@@ -751,55 +760,15 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card) ...@@ -751,55 +760,15 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
/* Power management */ /* 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 sdio_func *func = card->func;
struct lbs_private *priv = card->priv; struct lbs_private *priv = card->priv;
struct mmc_host *host = func->card->host;
int ret; int ret;
sdio_claim_host(func); sdio_claim_host(func);
sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
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;
/* /*
* Get rx_unit if the chip is SD8688 or newer. * 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) ...@@ -824,7 +793,7 @@ static int if_sdio_power_on(struct if_sdio_card *card)
*/ */
ret = sdio_claim_irq(func, if_sdio_interrupt); ret = sdio_claim_irq(func, if_sdio_interrupt);
if (ret) if (ret)
goto disable; goto release;
/* /*
* Enable interrupts now that everything is set up * Enable interrupts now that everything is set up
...@@ -850,11 +819,79 @@ static int if_sdio_power_on(struct if_sdio_card *card) ...@@ -850,11 +819,79 @@ static int if_sdio_power_on(struct if_sdio_card *card)
} }
priv->fw_ready = 1; 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: release_irq:
sdio_release_irq(func); 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: disable:
sdio_disable_func(func); sdio_disable_func(func);
release: release:
...@@ -1061,11 +1098,17 @@ static int if_sdio_power_save(struct lbs_private *priv) ...@@ -1061,11 +1098,17 @@ static int if_sdio_power_save(struct lbs_private *priv)
static int if_sdio_power_restore(struct lbs_private *priv) static int if_sdio_power_restore(struct lbs_private *priv)
{ {
struct if_sdio_card *card = priv->card; struct if_sdio_card *card = priv->card;
int r;
/* Make sure the card will not be powered off by runtime PM */ /* Make sure the card will not be powered off by runtime PM */
pm_runtime_get_sync(&card->func->dev); 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, ...@@ -1166,6 +1209,7 @@ static int if_sdio_probe(struct sdio_func *func,
spin_lock_init(&card->lock); spin_lock_init(&card->lock);
card->workqueue = create_workqueue("libertas_sdio"); card->workqueue = create_workqueue("libertas_sdio");
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
init_waitqueue_head(&card->pwron_waitq);
/* Check if we support this card */ /* Check if we support this card */
for (i = 0; i < ARRAY_SIZE(fw_table); i++) { for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
...@@ -1207,14 +1251,6 @@ static int if_sdio_probe(struct sdio_func *func, ...@@ -1207,14 +1251,6 @@ static int if_sdio_probe(struct sdio_func *func,
if (ret) if (ret)
goto err_activate_card; 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: out:
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment