diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/card/block.c | 14 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 63 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 23 |
3 files changed, 94 insertions, 6 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 370472797ff..c0cb225bbb4 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -851,16 +851,18 @@ out: static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + int ret = 0; + + ret = mmc_flush_cache(card); + if (ret) + ret = -EIO; - /* - * No-op, only service this because we need REQ_FUA for reliable - * writes. - */ spin_lock_irq(&md->lock); - __blk_end_request_all(req, 0); + __blk_end_request_all(req, ret); spin_unlock_irq(&md->lock); - return 1; + return ret ? 0 : 1; } /* diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 772de2cdfd1..235bb6a1f97 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2158,6 +2158,65 @@ int mmc_card_can_sleep(struct mmc_host *host) } EXPORT_SYMBOL(mmc_card_can_sleep); +/* + * Flush the cache to the non-volatile storage. + */ +int mmc_flush_cache(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + + if (!(host->caps2 & MMC_CAP2_CACHE_CTRL)) + return err; + + if (mmc_card_mmc(card) && + (card->ext_csd.cache_size > 0) && + (card->ext_csd.cache_ctrl & 1)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_FLUSH_CACHE, 1, 0); + if (err) + pr_err("%s: cache flush error %d\n", + mmc_hostname(card->host), err); + } + + return err; +} +EXPORT_SYMBOL(mmc_flush_cache); + +/* + * Turn the cache ON/OFF. + * Turning the cache OFF shall trigger flushing of the data + * to the non-volatile storage. + */ +int mmc_cache_ctrl(struct mmc_host *host, u8 enable) +{ + struct mmc_card *card = host->card; + int err = 0; + + if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) || + mmc_card_is_removable(host)) + return err; + + if (card && mmc_card_mmc(card) && + (card->ext_csd.cache_size > 0)) { + enable = !!enable; + + if (card->ext_csd.cache_ctrl ^ enable) + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CACHE_CTRL, enable, 0); + if (err) + pr_err("%s: cache %s error %d\n", + mmc_hostname(card->host), + enable ? "on" : "off", + err); + else + card->ext_csd.cache_ctrl = enable; + } + + return err; +} +EXPORT_SYMBOL(mmc_cache_ctrl); + #ifdef CONFIG_PM /** @@ -2172,6 +2231,9 @@ int mmc_suspend_host(struct mmc_host *host) cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); + err = mmc_cache_ctrl(host, 0); + if (err) + goto out; mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { @@ -2197,6 +2259,7 @@ int mmc_suspend_host(struct mmc_host *host) if (!err && !mmc_card_keep_power(host)) mmc_power_off(host); +out: return err; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 79390151920..de5900aa81c 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -470,6 +470,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } else card->ext_csd.generic_cmd6_time = 0; + card->ext_csd.cache_size = + ext_csd[EXT_CSD_CACHE_SIZE + 0] << 0 | + ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 | + ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 | + ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24; + out: return err; } @@ -1020,6 +1026,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + /* + * If cache size is higher than 0, this indicates + * the existence of cache and it can be turned on. + */ + if ((host->caps2 & MMC_CAP2_CACHE_CTRL) && + card->ext_csd.cache_size > 0) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CACHE_CTRL, 1, 0); + if (err && err != -EBADMSG) + goto free_card; + + /* + * Only if no error, cache is turned on successfully. + */ + card->ext_csd.cache_ctrl = err ? 0 : 1; + } + if (!oldcard) host->card = card; |