diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-10 11:16:00 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-10 11:16:00 -0700 |
commit | bfffbea1aaeeb1eb6500c83ff9653416daa5b490 (patch) | |
tree | 1325ecb176b7a3d04924d6a1563b15ad6085d36a /drivers/mmc/core | |
parent | 34ae0a6f05aee9f51fca17001b4a90703d434ae1 (diff) | |
parent | 01ebea1b411aafc8eab440bf1d2037f01bbed99b (diff) | |
download | blackbird-op-linux-bfffbea1aaeeb1eb6500c83ff9653416daa5b490.tar.gz blackbird-op-linux-bfffbea1aaeeb1eb6500c83ff9653416daa5b490.zip |
Merge tag 'mmc-updates-for-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball:
"MMC highlights for 3.11:
Core:
- Add support for eMMC 5.1 devices
- Add MMC_CAP_AGGRESSIVE_PM capability for aggressive power
management of eMMC/SD between requests, using runtime PM
- Add an ioctl to perform the eMMC 4.5 Sanitize command. Sample code
at:
git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc-utils.git
Drivers:
- dw_mmc: Add support for Rockchip's Cortex-A9 SoCs
- dw_mmc: Add support for Altera SoCFPGAs
- sdhci-esdhc-imx: Add support for 8-bit bus width, non-removable
cards
- sdhci-bcm-kona: New driver for Broadcom Kona (281xx) SoCs
- sdhi/tmio: Add DT DMA support"
* tag 'mmc-updates-for-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (87 commits)
mmc: bcm281xx SDHCI driver
mmc: sdhci: add card_event callback to sdhci
mmc: core: Fixup Oops for SDIO shutdown
mmc: sdhci-pci: add another device id
mmc: esdhc: Fix bug when writing to SDHCI_HOST_CONTROL register
mmc: esdhc: Add support for 8-bit bus width and non-removable card
mmc: core: production year for eMMC 4.41 and later
mmc: omap: remove unnecessary #if 0's
mmc: sdhci: fix ctrl_2 on super-speed selection
mmc: dw_mmc-pltfm: add Rockchip variant
mmc: dw_mmc-pltfm: move probe and remove below dt match table
mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove
mmc: sdhci-acpi: add support for eMMC hardware reset for HID 80860F14
mmc: sdhci-pci: add support for eMMC hardware reset for BYT eMMC.
mmc: dw_mmc: Add support DW SD/MMC driver on SOCFPGA
mmc: sdhci: fix caps2 for HS200
sdhci-pxav3: Fix runtime PM initialization
mmc: core: Add DT-bindings for MMC_CAP2_FULL_PWR_CYCLE
mmc: core: Invent MMC_CAP2_FULL_PWR_CYCLE
mmc: core: Enable power_off_notify for eMMC shutdown sequence
...
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/bus.c | 52 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 200 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 7 | ||||
-rw-r--r-- | drivers/mmc/core/debugfs.c | 8 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 32 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 249 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 36 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 68 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 76 |
10 files changed, 457 insertions, 272 deletions
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 9d5c71125576..704bf66f5873 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 c40396f23202..49a5bca418bd 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->card) && - (host->caps & MMC_CAP_POWER_OFF_CARD)) { - pm_runtime_disable(&host->card->dev); - pm_runtime_set_active(&host->card->dev); - pm_runtime_enable(&host->card->dev); - } - } - BUG_ON(!host->bus_ops->resume); - err = host->bus_ops->resume(host); - if (err) { - pr_warning("%s: error %d during resume " - "(card was removed?)\n", - mmc_hostname(host), err); - err = 0; - } - } - host->pm_flags &= ~MMC_PM_KEEP_POWER; - mmc_bus_put(host); - - return err; + /* This function is deprecated */ + return 0; } EXPORT_SYMBOL(mmc_resume_host); @@ -2727,29 +2650,22 @@ int mmc_pm_notify(struct notifier_block *notify_block, switch (mode) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: - if (host->card && mmc_card_mmc(host->card) && - mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) { - pr_err("%s: didn't stop bkops\n", - mmc_hostname(host)); - return err; - } - mmc_card_clr_doing_bkops(host->card); - } - spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 1; spin_unlock_irqrestore(&host->lock, flags); cancel_delayed_work_sync(&host->detect); - if (!host->bus_ops || host->bus_ops->suspend) + if (!host->bus_ops) break; - /* Calling bus_ops->remove() with a claimed host can deadlock */ - if (host->bus_ops->remove) - host->bus_ops->remove(host); + /* Validate prerequisites for suspend */ + if (host->bus_ops->pre_suspend) + err = host->bus_ops->pre_suspend(host); + if (!err && host->bus_ops->suspend) + break; + /* Calling bus_ops->remove() with a claimed host can deadlock */ + host->bus_ops->remove(host); mmc_claim_host(host); mmc_detach_bus(host); mmc_power_off(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index b9f18a2a8874..5345d156493e 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -16,15 +16,17 @@ #define MMC_CMD_RETRIES 3 struct mmc_bus_ops { - int (*awake)(struct mmc_host *); - int (*sleep)(struct mmc_host *); void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); + int (*pre_suspend)(struct mmc_host *); int (*suspend)(struct mmc_host *); int (*resume)(struct mmc_host *); + int (*runtime_suspend)(struct mmc_host *); + int (*runtime_resume)(struct mmc_host *); int (*power_save)(struct mmc_host *); int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); + int (*shutdown)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); @@ -44,6 +46,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); +void mmc_power_up(struct mmc_host *host); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 35c2f85b1956..54829c0ed000 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -258,13 +258,13 @@ static int mmc_dbg_card_status_get(void *data, u64 *val) u32 status; int ret; - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_send_status(data, &status); if (!ret) *val = status; - mmc_release_host(card->host); + mmc_put_card(card); return ret; } @@ -291,9 +291,9 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) goto out_free; } - mmc_claim_host(card->host); + mmc_get_card(card); err = mmc_send_ext_csd(card, ext_csd); - mmc_release_host(card->host); + mmc_put_card(card); if (err) goto out_free; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 2a3593d9f87d..6fb6f77450cb 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -306,7 +306,7 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) * parse the properties and set respective generic mmc-host flags and * parameters. */ -void mmc_of_parse(struct mmc_host *host) +int mmc_of_parse(struct mmc_host *host) { struct device_node *np; u32 bus_width; @@ -315,7 +315,7 @@ void mmc_of_parse(struct mmc_host *host) int len, ret, gpio; if (!host->parent || !host->parent->of_node) - return; + return 0; np = host->parent->of_node; @@ -338,6 +338,7 @@ void mmc_of_parse(struct mmc_host *host) default: dev_err(host->parent, "Invalid \"bus-width\" value %ud!\n", bus_width); + return -EINVAL; } /* f_max is obtained from the optional "max-frequency" property */ @@ -367,18 +368,22 @@ void mmc_of_parse(struct mmc_host *host) host->caps |= MMC_CAP_NEEDS_POLL; gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags); + if (gpio == -EPROBE_DEFER) + return gpio; if (gpio_is_valid(gpio)) { if (!(flags & OF_GPIO_ACTIVE_LOW)) gpio_inv_cd = true; ret = mmc_gpio_request_cd(host, gpio); - if (ret < 0) + if (ret < 0) { dev_err(host->parent, "Failed to request CD GPIO #%d: %d!\n", gpio, ret); - else + return ret; + } else { dev_info(host->parent, "Got CD GPIO #%d.\n", gpio); + } } if (explicit_inv_cd ^ gpio_inv_cd) @@ -389,14 +394,23 @@ void mmc_of_parse(struct mmc_host *host) explicit_inv_wp = of_property_read_bool(np, "wp-inverted"); gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); + if (gpio == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto out; + } if (gpio_is_valid(gpio)) { if (!(flags & OF_GPIO_ACTIVE_LOW)) gpio_inv_wp = true; ret = mmc_gpio_request_ro(host, gpio); - if (ret < 0) + if (ret < 0) { dev_err(host->parent, "Failed to request WP GPIO: %d!\n", ret); + goto out; + } else { + dev_info(host->parent, "Got WP GPIO #%d.\n", + gpio); + } } if (explicit_inv_wp ^ gpio_inv_wp) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; @@ -409,10 +423,18 @@ void mmc_of_parse(struct mmc_host *host) host->caps |= MMC_CAP_POWER_OFF_CARD; if (of_find_property(np, "cap-sdio-irq", &len)) host->caps |= MMC_CAP_SDIO_IRQ; + if (of_find_property(np, "full-pwr-cycle", &len)) + host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE; if (of_find_property(np, "keep-power-in-suspend", &len)) host->pm_caps |= MMC_PM_KEEP_POWER; if (of_find_property(np, "enable-sdio-wakeup", &len)) host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + + return 0; + +out: + mmc_gpio_free_cd(host); + return ret; } EXPORT_SYMBOL(mmc_of_parse); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0cbd1effe960..6d02012a1d0b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -293,7 +293,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } card->ext_csd.rev = ext_csd[EXT_CSD_REV]; - if (card->ext_csd.rev > 6) { + if (card->ext_csd.rev > 7) { pr_err("%s: unrecognised EXT_CSD revision %d\n", mmc_hostname(card->host), card->ext_csd.rev); err = -EINVAL; @@ -461,9 +461,31 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) */ card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP]; card->ext_csd.boot_ro_lockable = true; + + /* Save power class values */ + card->ext_csd.raw_pwr_cl_52_195 = + ext_csd[EXT_CSD_PWR_CL_52_195]; + card->ext_csd.raw_pwr_cl_26_195 = + ext_csd[EXT_CSD_PWR_CL_26_195]; + card->ext_csd.raw_pwr_cl_52_360 = + ext_csd[EXT_CSD_PWR_CL_52_360]; + card->ext_csd.raw_pwr_cl_26_360 = + ext_csd[EXT_CSD_PWR_CL_26_360]; + card->ext_csd.raw_pwr_cl_200_195 = + ext_csd[EXT_CSD_PWR_CL_200_195]; + card->ext_csd.raw_pwr_cl_200_360 = + ext_csd[EXT_CSD_PWR_CL_200_360]; + card->ext_csd.raw_pwr_cl_ddr_52_195 = + ext_csd[EXT_CSD_PWR_CL_DDR_52_195]; + card->ext_csd.raw_pwr_cl_ddr_52_360 = + ext_csd[EXT_CSD_PWR_CL_DDR_52_360]; } if (card->ext_csd.rev >= 5) { + /* Adjust production date as per JEDEC JESD84-B451 */ + if (card->cid.year < 2010) + card->cid.year += 16; + /* check whether the eMMC card supports BKOPS */ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { card->ext_csd.bkops = 1; @@ -607,7 +629,23 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) (card->ext_csd.raw_sectors[2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) && (card->ext_csd.raw_sectors[3] == - bw_ext_csd[EXT_CSD_SEC_CNT + 3])); + bw_ext_csd[EXT_CSD_SEC_CNT + 3]) && + (card->ext_csd.raw_pwr_cl_52_195 == + bw_ext_csd[EXT_CSD_PWR_CL_52_195]) && + (card->ext_csd.raw_pwr_cl_26_195 == + bw_ext_csd[EXT_CSD_PWR_CL_26_195]) && + (card->ext_csd.raw_pwr_cl_52_360 == + bw_ext_csd[EXT_CSD_PWR_CL_52_360]) && + (card->ext_csd.raw_pwr_cl_26_360 == + bw_ext_csd[EXT_CSD_PWR_CL_26_360]) && + (card->ext_csd.raw_pwr_cl_200_195 == + bw_ext_csd[EXT_CSD_PWR_CL_200_195]) && + (card->ext_csd.raw_pwr_cl_200_360 == + bw_ext_csd[EXT_CSD_PWR_CL_200_360]) && + (card->ext_csd.raw_pwr_cl_ddr_52_195 == + bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) && + (card->ext_csd.raw_pwr_cl_ddr_52_360 == + bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360])); if (err) err = -EINVAL; @@ -676,11 +714,10 @@ static struct device_type mmc_type = { * mmc_switch command. */ static int mmc_select_powerclass(struct mmc_card *card, - unsigned int bus_width, u8 *ext_csd) + unsigned int bus_width) { int err = 0; - unsigned int pwrclass_val; - unsigned int index = 0; + unsigned int pwrclass_val = 0; struct mmc_host *host; BUG_ON(!card); @@ -688,9 +725,6 @@ static int mmc_select_powerclass(struct mmc_card *card, host = card->host; BUG_ON(!host); - if (ext_csd == NULL) - return 0; - /* Power class selection is supported for versions >= 4.0 */ if (card->csd.mmca_vsn < CSD_SPEC_VER_4) return 0; @@ -702,13 +736,13 @@ static int mmc_select_powerclass(struct mmc_card *card, switch (1 << host->ios.vdd) { case MMC_VDD_165_195: if (host->ios.clock <= 26000000) - index = EXT_CSD_PWR_CL_26_195; + pwrclass_val = card->ext_csd.raw_pwr_cl_26_195; else if (host->ios.clock <= 52000000) - index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? - EXT_CSD_PWR_CL_52_195 : - EXT_CSD_PWR_CL_DDR_52_195; + pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? + card->ext_csd.raw_pwr_cl_52_195 : + card->ext_csd.raw_pwr_cl_ddr_52_195; else if (host->ios.clock <= 200000000) - index = EXT_CSD_PWR_CL_200_195; + pwrclass_val = card->ext_csd.raw_pwr_cl_200_195; break; case MMC_VDD_27_28: case MMC_VDD_28_29: @@ -720,13 +754,13 @@ static int mmc_select_powerclass(struct mmc_card *card, case MMC_VDD_34_35: case MMC_VDD_35_36: if (host->ios.clock <= 26000000) - index = EXT_CSD_PWR_CL_26_360; + pwrclass_val = card->ext_csd.raw_pwr_cl_26_360; else if (host->ios.clock <= 52000000) - index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? - EXT_CSD_PWR_CL_52_360 : - EXT_CSD_PWR_CL_DDR_52_360; + pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? + card->ext_csd.raw_pwr_cl_52_360 : + card->ext_csd.raw_pwr_cl_ddr_52_360; else if (host->ios.clock <= 200000000) - index = EXT_CSD_PWR_CL_200_360; + pwrclass_val = card->ext_csd.raw_pwr_cl_200_360; break; default: pr_warning("%s: Voltage range not supported " @@ -734,8 +768,6 @@ static int mmc_select_powerclass(struct mmc_card *card, return -EINVAL; } - pwrclass_val = ext_csd[index]; - if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8)) pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >> EXT_CSD_PWR_CL_8BIT_SHIFT; @@ -1013,11 +1045,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* - * If the host supports the power_off_notify capability then - * set the notification byte in the ext_csd register of device + * Enable power_off_notification byte in the ext_csd register */ - if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) && - (card->ext_csd.rev >= 6)) { + if (card->ext_csd.rev >= 6) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_POWER_OFF_NOTIFICATION, EXT_CSD_POWER_ON, @@ -1131,7 +1161,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; - err = mmc_select_powerclass(card, ext_csd_bits, ext_csd); + err = mmc_select_powerclass(card, ext_csd_bits); if (err) pr_warning("%s: power class selection to bus width %d" " failed\n", mmc_hostname(card->host), @@ -1164,8 +1194,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, bus_width = bus_widths[idx]; if (bus_width == MMC_BUS_WIDTH_1) ddr = 0; /* no DDR for 1-bit width */ - err = mmc_select_powerclass(card, ext_csd_bits[idx][0], - ext_csd); + err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); if (err) pr_warning("%s: power class selection to " "bus width %d failed\n", @@ -1195,8 +1224,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } if (!err && ddr) { - err = mmc_select_powerclass(card, ext_csd_bits[idx][1], - ext_csd); + err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); if (err) pr_warning("%s: power class selection to " "bus width %d ddr %d failed\n", @@ -1321,6 +1349,45 @@ err: return err; } +static int mmc_can_sleep(struct mmc_card *card) +{ + return (card && card->ext_csd.rev >= 3); +} + +static int mmc_sleep(struct mmc_host *host) +{ + struct mmc_command cmd = {0}; + struct mmc_card *card = host->card; + int err; + + if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) + return 0; + + err = mmc_deselect_cards(host); + if (err) + return err; + + cmd.opcode = MMC_SLEEP_AWAKE; + cmd.arg = card->rca << 16; + cmd.arg |= 1 << 15; + + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) + return err; + + /* + * If the host does not wait while the card signals busy, then we will + * will have to wait the sleep/awake timeout. Note, we cannot use the + * SEND_STATUS command to poll the status because that command (and most + * others) is invalid while the card sleeps. + */ + if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); + + return err; +} + static int mmc_can_poweroff_notify(const struct mmc_card *card) { return card && @@ -1380,14 +1447,14 @@ static void mmc_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_claim_host(host); + mmc_get_card(host->card); /* * Just check if our card has been removed. */ err = _mmc_detect_card_removed(host); - mmc_release_host(host); + mmc_put_card(host->card); if (err) { mmc_remove(host); @@ -1399,36 +1466,60 @@ static void mmc_detect(struct mmc_host *host) } } -/* - * Suspend callback from host. - */ -static int mmc_suspend(struct mmc_host *host) +static int _mmc_suspend(struct mmc_host *host, bool is_suspend) { int err = 0; + unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT : + EXT_CSD_POWER_OFF_LONG; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); + if (mmc_card_doing_bkops(host->card)) { + err = mmc_stop_bkops(host->card); + if (err) + goto out; + } + err = mmc_cache_ctrl(host, 0); if (err) goto out; - if (mmc_can_poweroff_notify(host->card)) - err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT); - else if (mmc_card_can_sleep(host)) - err = mmc_card_sleep(host); + if (mmc_can_poweroff_notify(host->card) && + ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend)) + err = mmc_poweroff_notify(host->card, notify_type); + else if (mmc_can_sleep(host->card)) + err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + if (!err) + mmc_power_off(host); out: mmc_release_host(host); return err; } /* + * Suspend callback from host. + */ +static int mmc_suspend(struct mmc_host *host) +{ + return _mmc_suspend(host, true); +} + +/* + * Shutdown callback + */ +static int mmc_shutdown(struct mmc_host *host) +{ + return _mmc_suspend(host, false); +} + +/* * Resume callback from host. * * This function tries to determine if the same card is still present @@ -1442,74 +1533,94 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); + mmc_power_up(host); + mmc_select_voltage(host, host->ocr); err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; } -static int mmc_power_restore(struct mmc_host *host) + +/* + * Callback for runtime_suspend. + */ +static int mmc_runtime_suspend(struct mmc_host *host) { - int ret; + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; - host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_claim_host(host); - ret = mmc_init_card(host, host->ocr, host->card); - mmc_release_host(host); - return ret; + err = mmc_suspend(host); + if (err) { + pr_err("%s: error %d doing aggessive suspend\n", + mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; } -static int mmc_sleep(struct mmc_host *host) +/* + * Callback for runtime_resume. + */ +static int mmc_runtime_resume(struct mmc_host *host) { - struct mmc_card *card = host->card; - int err = -ENOSYS; + int err; - if (card && card->ext_csd.rev >= 3) { - err = mmc_card_sleepawake(host, 1); - if (err < 0) - pr_debug("%s: Error %d while putting card into sleep", - mmc_hostname(host), err); - } + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; - return err; + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_resume(host); + if (err) + pr_err("%s: error %d doing aggessive resume\n", + mmc_hostname(host), err); + + mmc_release_host(host); + return 0; } -static int mmc_awake(struct mmc_host *host) +static int mmc_power_restore(struct mmc_host *host) { - struct mmc_card *card = host->card; - int err = -ENOSYS; + int ret; - if (card && card->ext_csd.rev >= 3) { - err = mmc_card_sleepawake(host, 0); - if (err < 0) - pr_debug("%s: Error %d while awaking sleeping card", - mmc_hostname(host), err); - } + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + mmc_claim_host(host); + ret = mmc_init_card(host, host->ocr, host->card); + mmc_release_host(host); - return err; + return ret; } static const struct mmc_bus_ops mmc_ops = { - .awake = mmc_awake, - .sleep = mmc_sleep, .remove = mmc_remove, .detect = mmc_detect, .suspend = NULL, .resume = NULL, .power_restore = mmc_power_restore, .alive = mmc_alive, + .shutdown = mmc_shutdown, }; static const struct mmc_bus_ops mmc_ops_unsafe = { - .awake = mmc_awake, - .sleep = mmc_sleep, .remove = mmc_remove, .detect = mmc_detect, .suspend = mmc_suspend, .resume = mmc_resume, + .runtime_suspend = mmc_runtime_suspend, + .runtime_resume = mmc_runtime_resume, .power_restore = mmc_power_restore, .alive = mmc_alive, + .shutdown = mmc_shutdown, }; static void mmc_attach_bus_ops(struct mmc_host *host) diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 49f04bc9d0eb..837fc7386e23 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -59,40 +59,6 @@ int mmc_deselect_cards(struct mmc_host *host) return _mmc_select_card(host, NULL); } -int mmc_card_sleepawake(struct mmc_host *host, int sleep) -{ - struct mmc_command cmd = {0}; - struct mmc_card *card = host->card; - int err; - - if (sleep) - mmc_deselect_cards(host); - - cmd.opcode = MMC_SLEEP_AWAKE; - cmd.arg = card->rca << 16; - if (sleep) - cmd.arg |= 1 << 15; - - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, 0); - if (err) - return err; - - /* - * If the host does not wait while the card signals busy, then we will - * will have to wait the sleep/awake timeout. Note, we cannot use the - * SEND_STATUS command to poll the status because that command (and most - * others) is invalid while the card sleeps. - */ - if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) - mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); - - if (!sleep) - err = mmc_select_card(card); - - return err; -} - int mmc_go_idle(struct mmc_host *host) { int err; @@ -431,6 +397,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, cmd.cmd_timeout_ms = timeout_ms; + if (index == EXT_CSD_SANITIZE_START) + cmd.sanitize_busy = true; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 3dd8941c2980..80ae9f4e0293 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -24,7 +24,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); -int mmc_card_sleepawake(struct mmc_host *host, int sleep); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9e645e19cec6..176d125f5b57 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -646,8 +646,13 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) if (err) goto out; - /* SPI mode doesn't define CMD19 */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) { + /* + * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and + * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. + */ + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && + (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || + card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) { mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); @@ -1037,14 +1042,14 @@ static void mmc_sd_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_claim_host(host); + mmc_get_card(host->card); /* * Just check if our card has been removed. */ err = _mmc_detect_card_removed(host); - mmc_release_host(host); + mmc_put_card(host->card); if (err) { mmc_sd_remove(host); @@ -1070,6 +1075,8 @@ static int mmc_sd_suspend(struct mmc_host *host) if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~MMC_STATE_HIGHSPEED; + if (!err) + mmc_power_off(host); mmc_release_host(host); return err; @@ -1089,12 +1096,61 @@ static int mmc_sd_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); + mmc_power_up(host); + mmc_select_voltage(host, host->ocr); err = mmc_sd_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; } +/* + * Callback for runtime_suspend. + */ +static int mmc_sd_runtime_suspend(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + err = mmc_sd_suspend(host); + if (err) { + pr_err("%s: error %d doing aggessive suspend\n", + mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; +} + +/* + * Callback for runtime_resume. + */ +static int mmc_sd_runtime_resume(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_sd_resume(host); + if (err) + pr_err("%s: error %d doing aggessive resume\n", + mmc_hostname(host), err); + + mmc_release_host(host); + return 0; +} + static int mmc_sd_power_restore(struct mmc_host *host) { int ret; @@ -1114,15 +1170,19 @@ static const struct mmc_bus_ops mmc_sd_ops = { .resume = NULL, .power_restore = mmc_sd_power_restore, .alive = mmc_sd_alive, + .shutdown = mmc_sd_suspend, }; static const struct mmc_bus_ops mmc_sd_ops_unsafe = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, + .runtime_suspend = mmc_sd_runtime_suspend, + .runtime_resume = mmc_sd_runtime_resume, .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, .power_restore = mmc_sd_power_restore, .alive = mmc_sd_alive, + .shutdown = mmc_sd_suspend, }; static void mmc_sd_attach_bus_ops(struct mmc_host *host) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 6889a821c1da..80d89cff7306 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -563,10 +563,18 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card) if (err) goto out; - /* Initialize and start re-tuning timer */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) + /* + * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and + * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. + */ + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && + ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) || + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) { + mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); + mmc_host_clk_release(card->host); + } out: @@ -902,11 +910,11 @@ out: } /* - * SDIO suspend. We need to suspend all functions separately. + * SDIO pre_suspend. We need to suspend all functions separately. * Therefore all registered functions must have drivers with suspend * and resume methods. Failing that we simply remove the whole card. */ -static int mmc_sdio_suspend(struct mmc_host *host) +static int mmc_sdio_pre_suspend(struct mmc_host *host) { int i, err = 0; @@ -917,8 +925,26 @@ static int mmc_sdio_suspend(struct mmc_host *host) if (!pmops || !pmops->suspend || !pmops->resume) { /* force removal of entire card in that case */ err = -ENOSYS; - } else - err = pmops->suspend(&func->dev); + break; + } + } + } + + return err; +} + +/* + * SDIO suspend. Suspend all functions separately. + */ +static int mmc_sdio_suspend(struct mmc_host *host) +{ + int i, err = 0; + + for (i = 0; i < host->card->sdio_funcs; i++) { + struct sdio_func *func = host->card->sdio_func[i]; + if (func && sdio_func_present(func) && func->dev.driver) { + const struct dev_pm_ops *pmops = func->dev.driver->pm; + err = pmops->suspend(&func->dev); if (err) break; } @@ -937,6 +963,9 @@ static int mmc_sdio_suspend(struct mmc_host *host) mmc_release_host(host); } + if (!err && !mmc_card_keep_power(host)) + mmc_power_off(host); + return err; } @@ -950,6 +979,23 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Basic card reinitialization. */ mmc_claim_host(host); + /* Restore power if needed */ + 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 (host->caps & MMC_CAP_POWER_OFF_CARD) { + pm_runtime_disable(&host->card->dev); + pm_runtime_set_active(&host->card->dev); + pm_runtime_enable(&host->card->dev); + } + } + /* No need to reinitialize powered-resumed nonremovable cards */ if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { sdio_reset(host); @@ -987,6 +1033,7 @@ static int mmc_sdio_resume(struct mmc_host *host) } } + host->pm_flags &= ~MMC_PM_KEEP_POWER; return err; } @@ -1051,11 +1098,28 @@ out: return ret; } +static int mmc_sdio_runtime_suspend(struct mmc_host *host) +{ + /* No references to the card, cut the power to it. */ + mmc_power_off(host); + return 0; +} + +static int mmc_sdio_runtime_resume(struct mmc_host *host) +{ + /* Restore power and re-initialize. */ + mmc_power_up(host); + return mmc_sdio_power_restore(host); +} + static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, + .pre_suspend = mmc_sdio_pre_suspend, .suspend = mmc_sdio_suspend, .resume = mmc_sdio_resume, + .runtime_suspend = mmc_sdio_runtime_suspend, + .runtime_resume = mmc_sdio_runtime_resume, .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, }; |