diff options
Diffstat (limited to 'drivers/net/wireless/libertas/if_sdio.c')
| -rw-r--r-- | drivers/net/wireless/libertas/if_sdio.c | 725 |
1 files changed, 474 insertions, 251 deletions
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 7a73f625273..33ceda296c9 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -26,14 +26,20 @@ * if_sdio_card_to_host() to pad the data. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> -#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/slab.h> #include <linux/firmware.h> #include <linux/netdevice.h> #include <linux/delay.h> #include <linux/mmc/card.h> #include <linux/mmc/sdio_func.h> #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" @@ -42,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 @@ -57,12 +65,6 @@ */ static u8 user_rmmod; -static char *lbs_helper_name = NULL; -module_param_named(helper_name, lbs_helper_name, charp, 0644); - -static char *lbs_fw_name = NULL; -module_param_named(fw_name, lbs_fw_name, charp, 0644); - static const struct sdio_device_id if_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, @@ -73,36 +75,32 @@ static const struct sdio_device_id if_sdio_ids[] = { MODULE_DEVICE_TABLE(sdio, if_sdio_ids); -struct if_sdio_model { - int model; - const char *helper; - const char *firmware; -}; - -static struct if_sdio_model if_sdio_models[] = { - { - /* 8385 */ - .model = IF_SDIO_MODEL_8385, - .helper = "sd8385_helper.bin", - .firmware = "sd8385.bin", - }, - { - /* 8686 */ - .model = IF_SDIO_MODEL_8686, - .helper = "sd8686_helper.bin", - .firmware = "sd8686.bin", - }, - { - /* 8688 */ - .model = IF_SDIO_MODEL_8688, - .helper = "sd8688_helper.bin", - .firmware = "sd8688.bin", - }, +#define MODEL_8385 0x04 +#define MODEL_8686 0x0b +#define MODEL_8688 0x10 + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" }, + { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" }, + { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" }, + { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" }, + { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" }, + { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" }, + { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" }, + { 0, NULL, NULL } }; +MODULE_FIRMWARE("libertas/sd8385_helper.bin"); +MODULE_FIRMWARE("libertas/sd8385.bin"); MODULE_FIRMWARE("sd8385_helper.bin"); MODULE_FIRMWARE("sd8385.bin"); +MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin"); +MODULE_FIRMWARE("libertas/sd8686_v9.bin"); +MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin"); +MODULE_FIRMWARE("libertas/sd8686_v8.bin"); MODULE_FIRMWARE("sd8686_helper.bin"); MODULE_FIRMWARE("sd8686.bin"); +MODULE_FIRMWARE("libertas/sd8688_helper.bin"); +MODULE_FIRMWARE("libertas/sd8688.bin"); MODULE_FIRMWARE("sd8688_helper.bin"); MODULE_FIRMWARE("sd8688.bin"); @@ -119,11 +117,10 @@ struct if_sdio_card { int model; unsigned long ioport; unsigned int scratch_reg; + bool started; + wait_queue_head_t pwron_waitq; - const char *helper; - const char *firmware; - - u8 buffer[65536]; + u8 buffer[65536] __attribute__((aligned(4))); spinlock_t lock; struct if_sdio_packet *packets; @@ -134,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 */ /********************************************************************/ @@ -182,11 +182,11 @@ static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) u16 rx_len; switch (card->model) { - case IF_SDIO_MODEL_8385: - case IF_SDIO_MODEL_8686: + case MODEL_8385: + case MODEL_8686: rx_len = if_sdio_read_scratch(card, &ret); break; - case IF_SDIO_MODEL_8688: + case MODEL_8688: default: /* for newer chipsets */ rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); if (!ret) @@ -283,7 +283,7 @@ static int if_sdio_handle_event(struct if_sdio_card *card, lbs_deb_enter(LBS_DEB_SDIO); - if (card->model == IF_SDIO_MODEL_8385) { + if (card->model == MODEL_8385) { event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); if (ret) goto out; @@ -312,12 +312,30 @@ out: return ret; } +static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition) +{ + u8 status; + unsigned long timeout; + int ret = 0; + + timeout = jiffies + HZ; + while (1) { + status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); + if (ret) + return ret; + if ((status & condition) == condition) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + mdelay(1); + } + return ret; +} + static int if_sdio_card_to_host(struct if_sdio_card *card) { int ret; - u8 status; u16 size, type, chunk; - unsigned long timeout; lbs_deb_enter(LBS_DEB_SDIO); @@ -332,19 +350,9 @@ static int if_sdio_card_to_host(struct if_sdio_card *card) goto out; } - timeout = jiffies + HZ; - while (1) { - status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); - if (ret) - goto out; - if (status & IF_SDIO_IO_RDY) - break; - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto out; - } - mdelay(1); - } + ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); + if (ret) + goto out; /* * The transfer must be in one transaction or the firmware @@ -400,7 +408,7 @@ static int if_sdio_card_to_host(struct if_sdio_card *card) out: if (ret) - lbs_pr_err("problem fetching packet from firmware\n"); + pr_err("problem fetching packet from firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); @@ -411,8 +419,6 @@ static void if_sdio_host_to_card_worker(struct work_struct *work) { struct if_sdio_card *card; struct if_sdio_packet *packet; - unsigned long timeout; - u8 status; int ret; unsigned long flags; @@ -432,25 +438,15 @@ static void if_sdio_host_to_card_worker(struct work_struct *work) sdio_claim_host(card->func); - timeout = jiffies + HZ; - while (1) { - status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); - if (ret) - goto release; - if (status & IF_SDIO_IO_RDY) - break; - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - mdelay(1); + ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); + if (ret == 0) { + ret = sdio_writesb(card->func, card->ioport, + packet->buffer, packet->nb); } - ret = sdio_writesb(card->func, card->ioport, - packet->buffer, packet->nb); if (ret) - goto release; -release: + pr_err("error %d sending packet to firmware\n", ret); + sdio_release_host(card->func); kfree(packet); @@ -463,11 +459,12 @@ release: /* Firmware */ /********************************************************************/ -static int if_sdio_prog_helper(struct if_sdio_card *card) +#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY) + +static int if_sdio_prog_helper(struct if_sdio_card *card, + const struct firmware *fw) { int ret; - u8 status; - const struct firmware *fw; unsigned long timeout; u8 *chunk_buffer; u32 chunk_size; @@ -476,16 +473,10 @@ static int if_sdio_prog_helper(struct if_sdio_card *card) lbs_deb_enter(LBS_DEB_SDIO); - ret = request_firmware(&fw, card->helper, &card->func->dev); - if (ret) { - lbs_pr_err("can't load helper firmware\n"); - goto out; - } - chunk_buffer = kzalloc(64, GFP_KERNEL); if (!chunk_buffer) { ret = -ENOMEM; - goto release_fw; + goto out; } sdio_claim_host(card->func); @@ -498,22 +489,16 @@ static int if_sdio_prog_helper(struct if_sdio_card *card) size = fw->size; while (size) { - timeout = jiffies + HZ; - while (1) { - status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); - if (ret) - goto release; - if ((status & IF_SDIO_IO_RDY) && - (status & IF_SDIO_DL_RDY)) - break; - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - mdelay(1); - } + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; + + /* On some platforms (like Davinci) the chip needs more time + * between helper blocks. + */ + mdelay(2); - chunk_size = min(size, (size_t)60); + chunk_size = min_t(size_t, size, 60); *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); memcpy(chunk_buffer + 4, firmware, chunk_size); @@ -566,23 +551,19 @@ static int if_sdio_prog_helper(struct if_sdio_card *card) release: sdio_release_host(card->func); kfree(chunk_buffer); -release_fw: - release_firmware(fw); out: if (ret) - lbs_pr_err("failed to load helper firmware\n"); + pr_err("failed to load helper firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; } -static int if_sdio_prog_real(struct if_sdio_card *card) +static int if_sdio_prog_real(struct if_sdio_card *card, + const struct firmware *fw) { int ret; - u8 status; - const struct firmware *fw; unsigned long timeout; u8 *chunk_buffer; u32 chunk_size; @@ -591,16 +572,10 @@ static int if_sdio_prog_real(struct if_sdio_card *card) lbs_deb_enter(LBS_DEB_SDIO); - ret = request_firmware(&fw, card->firmware, &card->func->dev); - if (ret) { - lbs_pr_err("can't load firmware\n"); - goto out; - } - chunk_buffer = kzalloc(512, GFP_KERNEL); if (!chunk_buffer) { ret = -ENOMEM; - goto release_fw; + goto out; } sdio_claim_host(card->func); @@ -615,11 +590,28 @@ static int if_sdio_prog_real(struct if_sdio_card *card) while (size) { timeout = jiffies + HZ; while (1) { - status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; + + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, + &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, + &ret) << 8; if (ret) goto release; - if ((status & IF_SDIO_IO_RDY) && - (status & IF_SDIO_DL_RDY)) + + /* + * For SD8688 wait until the length is not 0, 1 or 2 + * before downloading the first FW block, + * since BOOT code writes the register to indicate the + * helper/FW download winner, + * the value could be 1 or 2 (Func1 or Func2). + */ + if ((size != fw->size) || (req_size > 2)) break; if (time_after(jiffies, timeout)) { ret = -ETIMEDOUT; @@ -628,13 +620,6 @@ static int if_sdio_prog_real(struct if_sdio_card *card) mdelay(1); } - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); - if (ret) - goto release; - - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; - if (ret) - goto release; /* lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); */ @@ -654,7 +639,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card) req_size = size; while (req_size) { - chunk_size = min(req_size, (size_t)512); + chunk_size = min_t(size_t, req_size, 512); memcpy(chunk_buffer, firmware, chunk_size); /* @@ -701,18 +686,40 @@ static int if_sdio_prog_real(struct if_sdio_card *card) release: sdio_release_host(card->func); kfree(chunk_buffer); -release_fw: - release_firmware(fw); out: if (ret) - lbs_pr_err("failed to load firmware\n"); + pr_err("failed to load firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", 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) + return; + + lbs_deb_sdio("Helper firmware loaded\n"); + + ret = if_sdio_prog_real(card, mainfw); + if (ret) + return; + + lbs_deb_sdio("Firmware loaded\n"); + if_sdio_finish_power_on(card); +} + static int if_sdio_prog_firmware(struct if_sdio_card *card) { int ret; @@ -720,40 +727,214 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card) lbs_deb_enter(LBS_DEB_SDIO); + /* + * Disable interrupts + */ + sdio_claim_host(card->func); + sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret); + sdio_release_host(card->func); + sdio_claim_host(card->func); scratch = if_sdio_read_scratch(card, &ret); sdio_release_host(card->func); + lbs_deb_sdio("firmware status = %#x\n", scratch); + lbs_deb_sdio("scratch ret = %d\n", ret); + if (ret) goto out; - lbs_deb_sdio("firmware status = %#x\n", scratch); + /* + * The manual clearly describes that FEDC is the right code to use + * to detect firmware presence, but for SD8686 it is not that simple. + * Scratch is also used to store the RX packet length, so we lose + * the FEDC value early on. So we use a non-zero check in order + * to validate firmware presence. + * Additionally, the SD8686 in the Gumstix always has the high scratch + * bit set, even when the firmware is not loaded. So we have to + * exclude that from the test. + */ 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"); + if_sdio_finish_power_on(card); + return 0; } - ret = if_sdio_prog_helper(card); + ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, + fw_table, if_sdio_do_prog_firmware); + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +/********************************************************************/ +/* Power management */ +/********************************************************************/ + +/* 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; + int ret; + + sdio_claim_host(func); + sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); + + /* + * 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 out; + goto release; - ret = if_sdio_prog_real(card); + /* + * Enable interrupts now that everything is set up + */ + sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); if (ret) - goto out; + goto release_irq; -success: - sdio_claim_host(card->func); - sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); - sdio_release_host(card->func); - ret = 0; + sdio_release_host(func); -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + /* Set fw_ready before queuing any commands so that + * lbs_thread won't block from sending them to firmware. + */ + 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"); + } + + wake_up(&card->pwron_waitq); + + 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(&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_claim_host(func); + goto disable; + } + + return 0; + +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 */ /*******************************************************************/ @@ -848,7 +1029,7 @@ static int if_sdio_enter_deep_sleep(struct lbs_private *priv) ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), lbs_cmd_copyback, (unsigned long) &cmd); if (ret) - lbs_pr_err("DEEP_SLEEP cmd failed\n"); + netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n"); mdelay(200); return ret; @@ -864,7 +1045,7 @@ static int if_sdio_exit_deep_sleep(struct lbs_private *priv) sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); if (ret) - lbs_pr_err("sdio_writeb failed!\n"); + netdev_err(priv->dev, "sdio_writeb failed!\n"); sdio_release_host(card->func); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); @@ -881,7 +1062,7 @@ static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); if (ret) - lbs_pr_err("sdio_writeb failed!\n"); + netdev_err(priv->dev, "sdio_writeb failed!\n"); sdio_release_host(card->func); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); @@ -889,6 +1070,69 @@ static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) } +static struct mmc_host *reset_host; + +static void if_sdio_reset_card_worker(struct work_struct *work) +{ + /* + * The actual reset operation must be run outside of lbs_thread. This + * is because mmc_remove_host() will cause the device to be instantly + * destroyed, and the libertas driver then needs to end lbs_thread, + * leading to a deadlock. + * + * We run it in a workqueue totally independent from the if_sdio_card + * instance for that reason. + */ + + pr_info("Resetting card..."); + mmc_remove_host(reset_host); + mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); + +static void if_sdio_reset_card(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + + if (work_pending(&card_reset_work)) + return; + + reset_host = card->func->card->host; + 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; + int r; + + /* Make sure the card will not be powered off by runtime PM */ + pm_runtime_get_sync(&card->func->dev); + + r = if_sdio_power_on(card); + if (r) + return r; + + wait_event(card->pwron_waitq, priv->fw_ready); + return 0; +} + + /*******************************************************************/ /* SDIO callbacks */ /*******************************************************************/ @@ -904,7 +1148,7 @@ static void if_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); - if (ret) + if (ret || !cause) goto out; lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); @@ -953,13 +1197,13 @@ static int if_sdio_probe(struct sdio_func *func, "ID: %x", &model) == 1) break; if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { - model = IF_SDIO_MODEL_8385; + model = MODEL_8385; break; } } if (i == func->card->num_info) { - lbs_pr_err("unable to identify card model\n"); + pr_err("unable to identify card model\n"); return -ENODEV; } @@ -971,13 +1215,13 @@ static int if_sdio_probe(struct sdio_func *func, card->model = model; switch (card->model) { - case IF_SDIO_MODEL_8385: + case MODEL_8385: card->scratch_reg = IF_SDIO_SCRATCH_OLD; break; - case IF_SDIO_MODEL_8686: + case MODEL_8686: card->scratch_reg = IF_SDIO_SCRATCH; break; - case IF_SDIO_MODEL_8688: + case MODEL_8688: default: /* for newer chipsets */ card->scratch_reg = IF_SDIO_FW_STATUS; break; @@ -986,56 +1230,19 @@ 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); - for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) { - if (card->model == if_sdio_models[i].model) + /* Check if we support this card */ + for (i = 0; i < ARRAY_SIZE(fw_table); i++) { + if (card->model == fw_table[i].model) break; } - - if (i == ARRAY_SIZE(if_sdio_models)) { - lbs_pr_err("unknown card model 0x%x\n", card->model); + if (i == ARRAY_SIZE(fw_table)) { + pr_err("unknown card model 0x%x\n", card->model); ret = -ENODEV; goto free; } - card->helper = if_sdio_models[i].helper; - card->firmware = if_sdio_models[i].firmware; - - if (lbs_helper_name) { - lbs_deb_sdio("overriding helper firmware: %s\n", - lbs_helper_name); - card->helper = lbs_helper_name; - } - - if (lbs_fw_name) { - lbs_deb_sdio("overriding firmware: %s\n", lbs_fw_name); - card->firmware = lbs_fw_name; - } - - sdio_claim_host(func); - - ret = sdio_enable_func(func); - if (ret) - goto release; - - ret = sdio_claim_irq(func, if_sdio_interrupt); - if (ret) - goto disable; - - 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, " @@ -1043,14 +1250,11 @@ static int if_sdio_probe(struct sdio_func *func, 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; @@ -1060,44 +1264,11 @@ static int if_sdio_probe(struct sdio_func *func, priv->enter_deep_sleep = if_sdio_enter_deep_sleep; 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; - priv->fw_ready = 1; - - sdio_claim_host(func); - - /* - * Get rx_unit if the chip is SD8688 or newer. - * SD8385 & SD8686 do not have rx_unit. - */ - if ((card->model != IF_SDIO_MODEL_8385) - && (card->model != IF_SDIO_MODEL_8686)) - card->rx_unit = if_sdio_read_rx_unit(card); - else - card->rx_unit = 0; - - /* - * Enable interrupts now that everything is set up - */ - sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); - sdio_release_host(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); + ret = if_sdio_power_on(card); if (ret) goto err_activate_card; @@ -1109,14 +1280,6 @@ out: 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) { @@ -1139,7 +1302,10 @@ static void if_sdio_remove(struct sdio_func *func) card = sdio_get_drvdata(func); - if (user_rmmod && (card->model == IF_SDIO_MODEL_8688)) { + /* 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 * multiple functions @@ -1152,23 +1318,17 @@ static void if_sdio_remove(struct sdio_func *func) 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"); + 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); - 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; @@ -1176,15 +1336,76 @@ static void if_sdio_remove(struct sdio_func *func) } kfree(card); - lbs_deb_leave(LBS_DEB_SDIO); } +static int if_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + int ret; + struct if_sdio_card *card = sdio_get_drvdata(func); + + mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); + + /* If we're powered off anyway, just let the mmc layer remove the + * card. */ + if (!lbs_iface_active(card->priv)) + return -ENOSYS; + + dev_info(dev, "%s: suspend: PM flags = 0x%x\n", + sdio_func_id(func), flags); + + /* If we aren't being asked to wake on anything, we should bail out + * and let the SD stack power down the card. + */ + if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { + dev_info(dev, "Suspend without wake params -- powering down card\n"); + return -ENOSYS; + } + + if (!(flags & MMC_PM_KEEP_POWER)) { + dev_err(dev, "%s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + return ret; + + ret = lbs_suspend(card->priv); + if (ret) + return ret; + + return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); +} + +static int if_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct if_sdio_card *card = sdio_get_drvdata(func); + int ret; + + dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); + + ret = lbs_resume(card->priv); + + return ret; +} + +static const struct dev_pm_ops if_sdio_pm_ops = { + .suspend = if_sdio_suspend, + .resume = if_sdio_resume, +}; + static struct sdio_driver if_sdio_driver = { .name = "libertas_sdio", .id_table = if_sdio_ids, .probe = if_sdio_probe, .remove = if_sdio_remove, + .drv = { + .pm = &if_sdio_pm_ops, + }, }; /*******************************************************************/ @@ -1217,6 +1438,8 @@ static void __exit if_sdio_exit_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(&card_reset_work); + sdio_unregister_driver(&if_sdio_driver); lbs_deb_leave(LBS_DEB_SDIO); |
