diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 12 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 64 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_io.c | 56 |
3 files changed, 120 insertions, 12 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 30acd526582..f4b97d3c3d0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1151,6 +1151,9 @@ void mmc_stop_host(struct mmc_host *host) cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); + /* clear pm flags now and let card drivers set them as needed */ + host->pm_flags = 0; + mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { if (host->bus_ops->remove) @@ -1273,12 +1276,13 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) mmc_claim_host(host); mmc_detach_bus(host); mmc_release_host(host); + host->pm_flags = 0; err = 0; } } mmc_bus_put(host); - if (!err) + if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER)) mmc_power_off(host); return err; @@ -1296,8 +1300,10 @@ int mmc_resume_host(struct mmc_host *host) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - mmc_power_up(host); - mmc_select_voltage(host, host->ocr); + if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { + mmc_power_up(host); + mmc_select_voltage(host, host->ocr); + } BUG_ON(!host->bus_ops->resume); err = host->bus_ops->resume(host); if (err) { diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 06b64085a35..2dd4cfe7ca1 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -188,6 +188,40 @@ static int sdio_disable_cd(struct mmc_card *card) } /* + * Devices that remain active during a system suspend are + * put back into 1-bit mode. + */ +static int sdio_disable_wide(struct mmc_card *card) +{ + int ret; + u8 ctrl; + + if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) + return 0; + + if (card->cccr.low_speed && !card->cccr.wide_bus) + return 0; + + ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) + return ret; + + if (!(ctrl & SDIO_BUS_WIDTH_4BIT)) + return 0; + + ctrl &= ~SDIO_BUS_WIDTH_4BIT; + ctrl |= SDIO_BUS_ASYNC_INT; + + ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); + if (ret) + return ret; + + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_1); + + return 0; +} + +/* * Test if the card supports high-speed mode and, if so, switch to it. */ static int sdio_enable_hs(struct mmc_card *card) @@ -224,7 +258,7 @@ static int sdio_enable_hs(struct mmc_card *card) * we're trying to reinitialise. */ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, - struct mmc_card *oldcard) + struct mmc_card *oldcard, int powered_resume) { struct mmc_card *card; int err; @@ -235,9 +269,11 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Inform the card of the voltage */ - err = mmc_send_io_op_cond(host, host->ocr, &ocr); - if (err) - goto err; + if (!powered_resume) { + err = mmc_send_io_op_cond(host, host->ocr, &ocr); + if (err) + goto err; + } /* * For SPI, enable CRC as appropriate. @@ -262,7 +298,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * For native busses: set card RCA and quit open drain mode. */ - if (!mmc_host_is_spi(host)) { + if (!powered_resume && !mmc_host_is_spi(host)) { err = mmc_send_relative_addr(host, &card->rca); if (err) goto remove; @@ -273,7 +309,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Select card, as all following commands rely on that. */ - if (!mmc_host_is_spi(host)) { + if (!powered_resume && !mmc_host_is_spi(host)) { err = mmc_select_card(card); if (err) goto remove; @@ -425,6 +461,12 @@ static int mmc_sdio_suspend(struct mmc_host *host) } } + if (!err && host->pm_flags & MMC_PM_KEEP_POWER) { + mmc_claim_host(host); + sdio_disable_wide(host->card); + mmc_release_host(host); + } + return err; } @@ -437,7 +479,13 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Basic card reinitialization. */ mmc_claim_host(host); - err = mmc_sdio_init_card(host, host->ocr, host->card); + err = mmc_sdio_init_card(host, host->ocr, host->card, + (host->pm_flags & MMC_PM_KEEP_POWER)); + if (!err) + /* We may have switched to 1-bit mode during suspend. */ + err = sdio_enable_wide(host->card); + if (!err && host->sdio_irqs) + mmc_signal_sdio_irq(host); mmc_release_host(host); /* @@ -507,7 +555,7 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) /* * Detect and init the card. */ - err = mmc_sdio_init_card(host, host->ocr, NULL); + err = mmc_sdio_init_card(host, host->ocr, NULL, 0); if (err) goto err; card = host->card; diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index f9aa8a7deff..ff27c8c7135 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -189,7 +189,12 @@ static inline unsigned int sdio_max_byte_size(struct sdio_func *func) { unsigned mval = min(func->card->host->max_seg_size, func->card->host->max_blk_size); - mval = min(mval, func->max_blksize); + + if (mmc_blksz_for_byte_mode(func->card)) + mval = min(mval, func->cur_blksize); + else + mval = min(mval, func->max_blksize); + return min(mval, 512u); /* maximum size for byte mode */ } @@ -635,3 +640,52 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, *err_ret = ret; } EXPORT_SYMBOL_GPL(sdio_f0_writeb); + +/** + * sdio_get_host_pm_caps - get host power management capabilities + * @func: SDIO function attached to host + * + * Returns a capability bitmask corresponding to power management + * features supported by the host controller that the card function + * might rely upon during a system suspend. The host doesn't need + * to be claimed, nor the function active, for this information to be + * obtained. + */ +mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func) +{ + BUG_ON(!func); + BUG_ON(!func->card); + + return func->card->host->pm_caps; +} +EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps); + +/** + * sdio_set_host_pm_flags - set wanted host power management capabilities + * @func: SDIO function attached to host + * + * Set a capability bitmask corresponding to wanted host controller + * power management features for the upcoming suspend state. + * This must be called, if needed, each time the suspend method of + * the function driver is called, and must contain only bits that + * were returned by sdio_get_host_pm_caps(). + * The host doesn't need to be claimed, nor the function active, + * for this information to be set. + */ +int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags) +{ + struct mmc_host *host; + + BUG_ON(!func); + BUG_ON(!func->card); + + host = func->card->host; + + if (flags & ~host->pm_caps) + return -EINVAL; + + /* function suspend methods are serialized, hence no lock needed */ + host->pm_flags |= flags; + return 0; +} +EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags); |