diff options
Diffstat (limited to 'drivers/mmc/core/core.c')
-rw-r--r-- | drivers/mmc/core/core.c | 154 |
1 files changed, 96 insertions, 58 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 57a2b40..bf18b6b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,7 +23,6 @@ #include <linux/log2.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> -#include <linux/pm_wakeup.h> #include <linux/suspend.h> #include <linux/fault-inject.h> #include <linux/random.h> @@ -302,7 +301,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) } err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true); + EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal); if (err) { pr_warn("%s: Error %d starting bkops\n", mmc_hostname(card->host), err); @@ -919,6 +918,31 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) EXPORT_SYMBOL(__mmc_claim_host); /** + * mmc_try_claim_host - try exclusively to claim a host + * @host: mmc host to claim + * + * Returns %1 if the host is claimed, %0 otherwise. + */ +int mmc_try_claim_host(struct mmc_host *host) +{ + int claimed_host = 0; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (!host->claimed || host->claimer == current) { + host->claimed = 1; + host->claimer = current; + host->claim_cnt += 1; + claimed_host = 1; + } + spin_unlock_irqrestore(&host->lock, flags); + if (host->ops->enable && claimed_host && host->claim_cnt == 1) + host->ops->enable(host); + return claimed_host; +} +EXPORT_SYMBOL(mmc_try_claim_host); + +/** * mmc_release_host - release a host * @host: mmc host to release * @@ -1358,31 +1382,22 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) { int bit; - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - dev_warn(mmc_dev(host), - "card claims to support voltages below defined range\n"); - ocr &= ~0x7F; - } - ocr &= host->ocr_avail; - if (!ocr) { - dev_warn(mmc_dev(host), "no support for card's volts\n"); - return 0; - } - if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) { - bit = ffs(ocr) - 1; + bit = ffs(ocr); + if (bit) { + bit -= 1; + ocr &= 3 << bit; - mmc_power_cycle(host, ocr); + + mmc_host_clk_hold(host); + host->ios.vdd = bit; + mmc_set_ios(host); + mmc_host_clk_release(host); } else { - bit = fls(ocr) - 1; - ocr &= 3 << bit; - if (bit != host->ios.vdd) - dev_warn(mmc_dev(host), "exceeding card's volts\n"); + pr_warning("%s: host doesn't support card's voltages\n", + mmc_hostname(host)); + ocr = 0; } return ocr; @@ -1407,7 +1422,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, u32 ocr) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) { struct mmc_command cmd = {0}; int err = 0; @@ -1489,7 +1504,7 @@ power_cycle: if (err) { pr_debug("%s: Signal voltage switch failed, " "power cycling card\n", mmc_hostname(host)); - mmc_power_cycle(host, ocr); + mmc_power_cycle(host); } mmc_host_clk_release(host); @@ -1530,14 +1545,22 @@ 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. */ -void mmc_power_up(struct mmc_host *host, u32 ocr) +void mmc_power_up(struct mmc_host *host) { + int bit; + if (host->ios.power_mode == MMC_POWER_ON) return; mmc_host_clk_hold(host); - host->ios.vdd = fls(ocr) - 1; + /* If ocr is set, we use it */ + if (host->ocr) + bit = ffs(host->ocr) - 1; + else + bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; if (mmc_host_is_spi(host)) host->ios.chip_select = MMC_CS_HIGH; else @@ -1581,6 +1604,13 @@ void mmc_power_off(struct mmc_host *host) host->ios.clock = 0; host->ios.vdd = 0; + + /* + * Reset ocr mask to be the highest possible voltage supported for + * this mmc host. This value will be used at next power up. + */ + host->ocr = 1 << (fls(host->ocr_avail) - 1); + if (!mmc_host_is_spi(host)) { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; @@ -1600,12 +1630,12 @@ void mmc_power_off(struct mmc_host *host) mmc_host_clk_release(host); } -void mmc_power_cycle(struct mmc_host *host, u32 ocr) +void mmc_power_cycle(struct mmc_host *host) { mmc_power_off(host); /* Wait at least 1 ms according to SD spec */ mmc_delay(1); - mmc_power_up(host, ocr); + mmc_power_up(host); } /* @@ -1693,28 +1723,6 @@ void mmc_detach_bus(struct mmc_host *host) mmc_bus_put(host); } -static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, - bool cd_irq) -{ -#ifdef CONFIG_MMC_DEBUG - unsigned long flags; - spin_lock_irqsave(&host->lock, flags); - WARN_ON(host->removed); - spin_unlock_irqrestore(&host->lock, flags); -#endif - - /* - * If the device is configured as wakeup, we prevent a new sleep for - * 5 s to give provision for user space to consume the event. - */ - if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) && - device_can_wakeup(mmc_dev(host))) - pm_wakeup_event(mmc_dev(host), 5000); - - host->detect_change = 1; - mmc_schedule_delayed_work(&host->detect, delay); -} - /** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. @@ -1727,8 +1735,16 @@ static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { - _mmc_detect_change(host, delay, true); +#ifdef CONFIG_MMC_DEBUG + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); + WARN_ON(host->removed); + spin_unlock_irqrestore(&host->lock, flags); +#endif + host->detect_change = 1; + mmc_schedule_delayed_work(&host->detect, delay); } + EXPORT_SYMBOL(mmc_detect_change); void mmc_init_erase(struct mmc_card *card) @@ -2318,7 +2334,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) pr_info("%s: %s: trying to init card at %u Hz\n", mmc_hostname(host), __func__, host->f_init); #endif - mmc_power_up(host, host->ocr_avail); + mmc_power_up(host); /* * Some eMMCs (with VCCQ always on) may not be reset after power up, so @@ -2407,7 +2423,7 @@ int mmc_detect_card_removed(struct mmc_host *host) * rescan handle the card removal. */ cancel_delayed_work(&host->detect); - _mmc_detect_change(host, 0, false); + mmc_detect_change(host, 0); } } @@ -2488,8 +2504,8 @@ void mmc_start_host(struct mmc_host *host) if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else - mmc_power_up(host, host->ocr_avail); - _mmc_detect_change(host, 0, false); + mmc_power_up(host); + mmc_detect_change(host, 0); } void mmc_stop_host(struct mmc_host *host) @@ -2567,7 +2583,7 @@ int mmc_power_restore_host(struct mmc_host *host) return -EINVAL; } - mmc_power_up(host, host->card->ocr); + mmc_power_up(host); ret = host->bus_ops->power_restore(host); mmc_bus_put(host); @@ -2641,6 +2657,28 @@ EXPORT_SYMBOL(mmc_cache_ctrl); #ifdef CONFIG_PM +/** + * mmc_suspend_host - suspend a host + * @host: mmc host + */ +int mmc_suspend_host(struct mmc_host *host) +{ + /* This function is deprecated */ + return 0; +} +EXPORT_SYMBOL(mmc_suspend_host); + +/** + * mmc_resume_host - resume a previously suspended host + * @host: mmc host + */ +int mmc_resume_host(struct mmc_host *host) +{ + /* This function is deprecated */ + return 0; +} +EXPORT_SYMBOL(mmc_resume_host); + /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able to sync the card. @@ -2686,7 +2724,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; spin_unlock_irqrestore(&host->lock, flags); - _mmc_detect_change(host, 0, false); + mmc_detect_change(host, 0); } |