Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 265433
b: refs/heads/master
c: 7e1f79a
h: refs/heads/master
i:
  265431: 6be0b74
v: v3
  • Loading branch information
Daniel Drake authored and John W. Linville committed Aug 9, 2011
1 parent 82fbc5e commit d708cfb
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 107 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d2e7b3425c474300318e1d28b10a93c2401b9255
refs/heads/master: 7e1f79a1f5fef8ac54def967b22a87909c74c8f1
277 changes: 171 additions & 106 deletions trunk/drivers/net/wireless/libertas/if_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/host.h>
#include <linux/pm_runtime.h>

#include "host.h"
#include "decl.h"
Expand All @@ -47,6 +48,8 @@
#include "cmd.h"
#include "if_sdio.h"

static void if_sdio_interrupt(struct sdio_func *func);

/* 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
Expand Down Expand Up @@ -757,6 +760,136 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
return ret;
}

/********************************************************************/
/* Power management */
/********************************************************************/

static int if_sdio_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;

/*
* Get rx_unit if the chip is SD8688 or newer.
* SD8385 & SD8686 do not have rx_unit.
*/
if ((card->model != MODEL_8385)
&& (card->model != MODEL_8686))
card->rx_unit = if_sdio_read_rx_unit(card);
else
card->rx_unit = 0;

/*
* Set up the interrupt handler late.
*
* If we set it up earlier, the (buggy) hardware generates a spurious
* interrupt, even before the interrupt has been enabled, with
* CCCR_INTx = 0.
*
* We register the interrupt handler late so that we can handle any
* spurious interrupts, and also to avoid generation of that known
* spurious interrupt in the first place.
*/
ret = sdio_claim_irq(func, if_sdio_interrupt);
if (ret)
goto disable;

/*
* Enable interrupts now that everything is set up
*/
sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
if (ret)
goto release_irq;

sdio_release_host(func);

/*
* FUNC_INIT is required for SD8688 WLAN/BT multiple functions
*/
if (card->model == 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))
netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
}

priv->fw_ready = 1;

return 0;

release_irq:
sdio_release_irq(func);
disable:
sdio_disable_func(func);
release:
sdio_release_host(func);
return ret;
}

static int if_sdio_power_off(struct if_sdio_card *card)
{
struct sdio_func *func = card->func;
struct lbs_private *priv = card->priv;

priv->fw_ready = 0;

sdio_claim_host(func);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);
return 0;
}


/*******************************************************************/
/* Libertas callbacks */
/*******************************************************************/
Expand Down Expand Up @@ -923,6 +1056,32 @@ static void if_sdio_reset_card(struct lbs_private *priv)
schedule_work(&card_reset_work);
}

static int if_sdio_power_save(struct lbs_private *priv)
{
struct if_sdio_card *card = priv->card;
int ret;

flush_workqueue(card->workqueue);

ret = if_sdio_power_off(card);

/* Let runtime PM know the card is powered off */
pm_runtime_put_sync(&card->func->dev);

return ret;
}

static int if_sdio_power_restore(struct lbs_private *priv)
{
struct if_sdio_card *card = priv->card;

/* 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);
}


/*******************************************************************/
/* SDIO callbacks */
/*******************************************************************/
Expand Down Expand Up @@ -976,7 +1135,6 @@ static int if_sdio_probe(struct sdio_func *func,
int ret, i;
unsigned int model;
struct if_sdio_packet *packet;
struct mmc_host *host = func->card->host;

lbs_deb_enter(LBS_DEB_SDIO);

Expand Down Expand Up @@ -1033,60 +1191,18 @@ static int if_sdio_probe(struct sdio_func *func,
goto free;
}

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 release_int;

reg |= SDIO_BUS_ECSI;
sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
if (ret)
goto release_int;
}

card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
if (ret)
goto release_int;

card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
if (ret)
goto release_int;

card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
if (ret)
goto release_int;

sdio_release_host(func);

sdio_set_drvdata(func, card);

lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
"device = 0x%X, model = 0x%X, ioport = 0x%X\n",
func->class, func->vendor, func->device,
model, (unsigned)card->ioport);

ret = if_sdio_prog_firmware(card);
if (ret)
goto reclaim;

priv = lbs_add_card(card, &func->dev);
if (!priv) {
ret = -ENOMEM;
goto reclaim;
goto free;
}

card->priv = priv;
Expand All @@ -1097,62 +1213,21 @@ static int if_sdio_probe(struct sdio_func *func,
priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
priv->reset_card = if_sdio_reset_card;
priv->power_save = if_sdio_power_save;
priv->power_restore = if_sdio_power_restore;

sdio_claim_host(func);

/*
* Get rx_unit if the chip is SD8688 or newer.
* SD8385 & SD8686 do not have rx_unit.
*/
if ((card->model != MODEL_8385)
&& (card->model != MODEL_8686))
card->rx_unit = if_sdio_read_rx_unit(card);
else
card->rx_unit = 0;

/*
* Set up the interrupt handler late.
*
* If we set it up earlier, the (buggy) hardware generates a spurious
* interrupt, even before the interrupt has been enabled, with
* CCCR_INTx = 0.
*
* We register the interrupt handler late so that we can handle any
* spurious interrupts, and also to avoid generation of that known
* spurious interrupt in the first place.
*/
ret = sdio_claim_irq(func, if_sdio_interrupt);
if (ret)
goto disable;

/*
* Enable interrupts now that everything is set up
*/
sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
sdio_release_host(func);
ret = if_sdio_power_on(card);
if (ret)
goto reclaim;

priv->fw_ready = 1;

/*
* FUNC_INIT is required for SD8688 WLAN/BT multiple functions
*/
if (card->model == 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))
netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
}
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);

Expand All @@ -1161,14 +1236,6 @@ static int if_sdio_probe(struct sdio_func *func,
err_activate_card:
flush_workqueue(card->workqueue);
lbs_remove_card(priv);
reclaim:
sdio_claim_host(func);
release_int:
sdio_release_irq(func);
disable:
sdio_disable_func(func);
release:
sdio_release_host(func);
free:
destroy_workqueue(card->workqueue);
while (card->packets) {
Expand All @@ -1195,6 +1262,9 @@ static void if_sdio_remove(struct sdio_func *func)

card = sdio_get_drvdata(func);

/* Undo decrement done above in if_sdio_probe */
pm_runtime_get_noresume(&func->dev);

if (user_rmmod && (card->model == MODEL_8688)) {
/*
* FUNC_SHUTDOWN is required for SD8688 WLAN/BT
Expand All @@ -1219,11 +1289,6 @@ static void if_sdio_remove(struct sdio_func *func)
flush_workqueue(card->workqueue);
destroy_workqueue(card->workqueue);

sdio_claim_host(func);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);

while (card->packets) {
packet = card->packets;
card->packets = card->packets->next;
Expand Down

0 comments on commit d708cfb

Please sign in to comment.