diff options
Diffstat (limited to 'drivers')
63 files changed, 1726 insertions, 780 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index dd27b0783d5..cd0b7f4a1ff 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -34,6 +34,7 @@ #include <linux/delay.h> #include <linux/capability.h> #include <linux/compat.h> +#include <linux/pm_runtime.h> #include <linux/mmc/ioctl.h> #include <linux/mmc/card.h> @@ -58,6 +59,8 @@ MODULE_ALIAS("mmc:block"); #define INAND_CMD38_ARG_SECTRIM1 0x81 #define INAND_CMD38_ARG_SECTRIM2 0x88 #define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ +#define MMC_SANITIZE_REQ_TIMEOUT 240000 +#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16) #define mmc_req_rel_wr(req) (((req->cmd_flags & REQ_FUA) || \ (req->cmd_flags & REQ_META)) && \ @@ -222,7 +225,7 @@ static ssize_t power_ro_lock_store(struct device *dev, md = mmc_blk_get(dev_to_disk(dev)); card = md->queue.card; - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, card->ext_csd.boot_ro_lock | @@ -233,7 +236,7 @@ static ssize_t power_ro_lock_store(struct device *dev, else card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN; - mmc_release_host(card->host); + mmc_put_card(card); if (!ret) { pr_info("%s: Locking boot partition ro until next power on\n", @@ -408,6 +411,35 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, return err; } +static int ioctl_do_sanitize(struct mmc_card *card) +{ + int err; + + if (!(mmc_can_sanitize(card) && + (card->host->caps2 & MMC_CAP2_SANITIZE))) { + pr_warn("%s: %s - SANITIZE is not supported\n", + mmc_hostname(card->host), __func__); + err = -EOPNOTSUPP; + goto out; + } + + pr_debug("%s: %s - SANITIZE IN PROGRESS...\n", + mmc_hostname(card->host), __func__); + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_SANITIZE_START, 1, + MMC_SANITIZE_REQ_TIMEOUT); + + if (err) + pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n", + mmc_hostname(card->host), __func__, err); + + pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host), + __func__); +out: + return err; +} + static int mmc_blk_ioctl_cmd(struct block_device *bdev, struct mmc_ioc_cmd __user *ic_ptr) { @@ -491,7 +523,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mrq.cmd = &cmd; - mmc_claim_host(card->host); + mmc_get_card(card); err = mmc_blk_part_switch(card, md); if (err) @@ -510,6 +542,17 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, goto cmd_rel_host; } + if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) && + (cmd.opcode == MMC_SWITCH)) { + err = ioctl_do_sanitize(card); + + if (err) + pr_err("%s: ioctl_do_sanitize() failed. err = %d", + __func__, err); + + goto cmd_rel_host; + } + mmc_wait_for_req(card->host, &mrq); if (cmd.error) { @@ -558,7 +601,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, } cmd_rel_host: - mmc_release_host(card->host); + mmc_put_card(card); cmd_done: mmc_blk_put(md); @@ -939,10 +982,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; - unsigned int from, nr, arg, trim_arg, erase_arg; + unsigned int from, nr, arg; int err = 0, type = MMC_BLK_SECDISCARD; - if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) { + if (!(mmc_can_secure_erase_trim(card))) { err = -EOPNOTSUPP; goto out; } @@ -950,23 +993,11 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, from = blk_rq_pos(req); nr = blk_rq_sectors(req); - /* The sanitize operation is supported at v4.5 only */ - if (mmc_can_sanitize(card)) { - erase_arg = MMC_ERASE_ARG; - trim_arg = MMC_TRIM_ARG; - } else { - erase_arg = MMC_SECURE_ERASE_ARG; - trim_arg = MMC_SECURE_TRIM1_ARG; - } + if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr)) + arg = MMC_SECURE_TRIM1_ARG; + else + arg = MMC_SECURE_ERASE_ARG; - if (mmc_erase_group_aligned(card, from, nr)) - arg = erase_arg; - else if (mmc_can_trim(card)) - arg = trim_arg; - else { - err = -EINVAL; - goto out; - } retry: if (card->quirks & MMC_QUIRK_INAND_CMD38) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1002,9 +1033,6 @@ retry: goto out; } - if (mmc_can_sanitize(card)) - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_SANITIZE_START, 1, 0); out_retry: if (err && !mmc_blk_reset(md, card->host, type)) goto retry; @@ -1895,7 +1923,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_blk_part_switch(card, md); if (ret) { @@ -1939,7 +1967,7 @@ out: * In case sepecial request, there is no reentry to * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'. */ - mmc_release_host(card->host); + mmc_put_card(card); return ret; } @@ -2158,6 +2186,14 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md) struct mmc_card *card; if (md) { + /* + * Flush remaining requests and free queues. It + * is freeing the queue that stops new requests + * from being accepted. + */ + mmc_cleanup_queue(&md->queue); + if (md->flags & MMC_BLK_PACKED_CMD) + mmc_packed_clean(&md->queue); card = md->queue.card; if (md->disk->flags & GENHD_FL_UP) { device_remove_file(disk_to_dev(md->disk), &md->force_ro); @@ -2166,14 +2202,8 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md) device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock); - /* Stop new requests from getting into the queue */ del_gendisk(md->disk); } - - /* Then flush out any already in there */ - mmc_cleanup_queue(&md->queue); - if (md->flags & MMC_BLK_PACKED_CMD) - mmc_packed_clean(&md->queue); mmc_blk_put(md); } } @@ -2336,6 +2366,19 @@ static int mmc_blk_probe(struct mmc_card *card) if (mmc_add_disk(part_md)) goto out; } + + pm_runtime_set_autosuspend_delay(&card->dev, 3000); + pm_runtime_use_autosuspend(&card->dev); + + /* + * Don't enable runtime PM for SD-combo cards here. Leave that + * decision to be taken during the SDIO init sequence instead. + */ + if (card->type != MMC_TYPE_SD_COMBO) { + pm_runtime_set_active(&card->dev); + pm_runtime_enable(&card->dev); + } + return 0; out: @@ -2349,20 +2392,24 @@ static void mmc_blk_remove(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); mmc_blk_remove_parts(card, md); + pm_runtime_get_sync(&card->dev); mmc_claim_host(card->host); mmc_blk_part_switch(card, md); mmc_release_host(card->host); + if (card->type != MMC_TYPE_SD_COMBO) + pm_runtime_disable(&card->dev); + pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); } -#ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card) +static int _mmc_blk_suspend(struct mmc_card *card) { struct mmc_blk_data *part_md; struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { + pm_runtime_get_sync(&card->dev); mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); @@ -2371,6 +2418,17 @@ static int mmc_blk_suspend(struct mmc_card *card) return 0; } +static void mmc_blk_shutdown(struct mmc_card *card) +{ + _mmc_blk_suspend(card); +} + +#ifdef CONFIG_PM +static int mmc_blk_suspend(struct mmc_card *card) +{ + return _mmc_blk_suspend(card); +} + static int mmc_blk_resume(struct mmc_card *card) { struct mmc_blk_data *part_md; @@ -2386,6 +2444,7 @@ static int mmc_blk_resume(struct mmc_card *card) list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); } + pm_runtime_put(&card->dev); } return 0; } @@ -2402,6 +2461,7 @@ static struct mmc_driver mmc_driver = { .remove = mmc_blk_remove, .suspend = mmc_blk_suspend, .resume = mmc_blk_resume, + .shutdown = mmc_blk_shutdown, }; static int __init mmc_blk_init(void) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 759714ed6be..a69df521627 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -3025,12 +3025,17 @@ static void mmc_test_remove(struct mmc_card *card) mmc_test_free_dbgfs_file(card); } +static void mmc_test_shutdown(struct mmc_card *card) +{ +} + static struct mmc_driver mmc_driver = { .drv = { .name = "mmc_test", }, .probe = mmc_test_probe, .remove = mmc_test_remove, + .shutdown = mmc_test_shutdown, }; static int __init mmc_test_init(void) diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 9447a0e970d..fa9632eb63f 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -173,7 +173,7 @@ static void mmc_queue_setup_discard(struct request_queue *q, /* granularity must not be greater than max. discard */ if (card->pref_erase > max_discard) q->limits.discard_granularity = 0; - if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card)) + if (mmc_can_secure_erase_trim(card)) queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q); } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 9d5c7112557..704bf66f587 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -122,15 +122,39 @@ static int mmc_bus_remove(struct device *dev) return 0; } +static void mmc_bus_shutdown(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret; + + if (dev->driver && drv->shutdown) + drv->shutdown(card); + + if (host->bus_ops->shutdown) { + ret = host->bus_ops->shutdown(host); + if (ret) + pr_warn("%s: error %d during shutdown\n", + mmc_hostname(host), ret); + } +} + #ifdef CONFIG_PM_SLEEP static int mmc_bus_suspend(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); - int ret = 0; + struct mmc_host *host = card->host; + int ret; - if (dev->driver && drv->suspend) + if (dev->driver && drv->suspend) { ret = drv->suspend(card); + if (ret) + return ret; + } + + ret = host->bus_ops->suspend(host); return ret; } @@ -138,10 +162,17 @@ static int mmc_bus_resume(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); - int ret = 0; + struct mmc_host *host = card->host; + int ret; + + ret = host->bus_ops->resume(host); + if (ret) + pr_warn("%s: error %d during resume (card was removed?)\n", + mmc_hostname(host), ret); if (dev->driver && drv->resume) ret = drv->resume(card); + return ret; } #endif @@ -151,15 +182,25 @@ static int mmc_bus_resume(struct device *dev) static int mmc_runtime_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret = 0; + + if (host->bus_ops->runtime_suspend) + ret = host->bus_ops->runtime_suspend(host); - return mmc_power_save_host(card->host); + return ret; } static int mmc_runtime_resume(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret = 0; + + if (host->bus_ops->runtime_resume) + ret = host->bus_ops->runtime_resume(host); - return mmc_power_restore_host(card->host); + return ret; } static int mmc_runtime_idle(struct device *dev) @@ -182,6 +223,7 @@ static struct bus_type mmc_bus_type = { .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, .remove = mmc_bus_remove, + .shutdown = mmc_bus_shutdown, .pm = &mmc_bus_pm_ops, }; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c40396f2320..49a5bca418b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -402,6 +402,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, context_info->is_done_rcv = false; context_info->is_new_req = false; cmd = mrq->cmd; + if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) { err = host->areq->err_check(host->card, @@ -436,6 +437,24 @@ static void mmc_wait_for_req_done(struct mmc_host *host, wait_for_completion(&mrq->completion); cmd = mrq->cmd; + + /* + * If host has timed out waiting for the sanitize + * to complete, card might be still in programming state + * so let's try to bring the card out of programming + * state. + */ + if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) { + if (!mmc_interrupt_hpi(host->card)) { + pr_warning("%s: %s: Interrupted sanitize\n", + mmc_hostname(host), __func__); + cmd->error = 0; + break; + } else { + pr_err("%s: %s: Failed to interrupt sanitize\n", + mmc_hostname(host), __func__); + } + } if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) break; @@ -952,6 +971,29 @@ void mmc_release_host(struct mmc_host *host) EXPORT_SYMBOL(mmc_release_host); /* + * This is a helper function, which fetches a runtime pm reference for the + * card device and also claims the host. + */ +void mmc_get_card(struct mmc_card *card) +{ + pm_runtime_get_sync(&card->dev); + mmc_claim_host(card->host); +} +EXPORT_SYMBOL(mmc_get_card); + +/* + * This is a helper function, which releases the host and drops the runtime + * pm reference for the card device. + */ +void mmc_put_card(struct mmc_card *card) +{ + mmc_release_host(card->host); + pm_runtime_mark_last_busy(&card->dev); + pm_runtime_put_autosuspend(&card->dev); +} +EXPORT_SYMBOL(mmc_put_card); + +/* * Internal function that does the actual ios call to the host driver, * optionally printing some debug output. */ @@ -1459,7 +1501,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ -static void mmc_power_up(struct mmc_host *host) +void mmc_power_up(struct mmc_host *host) { int bit; @@ -2325,14 +2367,13 @@ int mmc_detect_card_removed(struct mmc_host *host) * The card will be considered unchanged unless we have been asked to * detect a change or host requires polling to provide card detection. */ - if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) && - !(host->caps2 & MMC_CAP2_DETECT_ON_ERR)) + if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL)) return ret; host->detect_change = 0; if (!ret) { ret = _mmc_detect_card_removed(host); - if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) { + if (ret && (host->caps & MMC_CAP_NEEDS_POLL)) { /* * Schedule a detect work as soon as possible to let a * rescan handle the card removal. @@ -2442,9 +2483,7 @@ void mmc_stop_host(struct mmc_host *host) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { /* Calling bus_ops->remove() with a claimed host can deadlock */ - if (host->bus_ops->remove) - host->bus_ops->remove(host); - + host->bus_ops->remove(host); mmc_claim_host(host); mmc_detach_bus(host); mmc_power_off(host); @@ -2509,52 +2548,6 @@ int mmc_power_restore_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_power_restore_host); -int mmc_card_awake(struct mmc_host *host) -{ - int err = -ENOSYS; - - if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) - return 0; - - mmc_bus_get(host); - - if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) - err = host->bus_ops->awake(host); - - mmc_bus_put(host); - - return err; -} -EXPORT_SYMBOL(mmc_card_awake); - -int mmc_card_sleep(struct mmc_host *host) -{ - int err = -ENOSYS; - - if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) - return 0; - - mmc_bus_get(host); - - if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep) - err = host->bus_ops->sleep(host); - - mmc_bus_put(host); - - return err; -} -EXPORT_SYMBOL(mmc_card_sleep); - -int mmc_card_can_sleep(struct mmc_host *host) -{ - struct mmc_card *card = host->card; - - if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3) - return 1; - return 0; -} -EXPORT_SYMBOL(mmc_card_can_sleep); - /* * Flush the cache to the non-volatile storage. */ @@ -2626,48 +2619,9 @@ EXPORT_SYMBOL(mmc_cache_ctrl); */ int mmc_suspend_host(struct mmc_host *host) { - int err = 0; - - cancel_delayed_work(&host->detect); - mmc_flush_scheduled_work(); - - mmc_bus_get(host); - if (host->bus_ops && !host->bus_dead) { - if (host->bus_ops->suspend) { - if (mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) - goto out; - } - err = host->bus_ops->suspend(host); - } - - if (err == -ENOSYS || !host->bus_ops->resume) { - /* - * We simply "remove" the card in this case. - * It will be redetected on resume. (Calling - * bus_ops->remove() with a claimed host can - * deadlock.) - */ - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_power_off(host); - mmc_release_host(host); - host->pm_flags = 0; - err = 0; - } - } - mmc_bus_put(host); - - if (!err && !mmc_card_keep_power(host)) - mmc_power_off(host); - -out: - return err; + /* This function is deprecated */ + return 0; } - EXPORT_SYMBOL(mmc_suspend_host); /** @@ -2676,39 +2630,8 @@ EXPORT_SYMBOL(mmc_suspend_host); */ int mmc_resume_host(struct mmc_host *host) { - int err = 0; - - mmc_bus_get(host); - if (host->bus_ops && !host->bus_dead) { - if (!mmc_card_keep_power(host)) { - mmc_power_up(host); - mmc_select_voltage(host, host->ocr); - /* - * Tell runtime PM core we just powered up the card, - * since it still believes the card is powered off. - * Note that currently runtime PM is only enabled - * for SDIO cards that are MMC_CAP_POWER_OFF_CARD - */ - if (mmc_card_sdio(host- |