diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-18 14:47:30 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-18 14:47:30 -0800 |
commit | c2ac2ae44d4c32382d001672021116e771bef4c9 (patch) | |
tree | 39a6ab0a118f562bb58ebc9e4c9cb709ac6ce29a /drivers | |
parent | 2d3c627502f2a9b0a7de06a5a2df2365542a72c9 (diff) | |
parent | e395c4387c746b4cc7aace4c44baecd7e69a3249 (diff) |
Merge tag 'mmc-updates-for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball:
"MMC highlights for 3.13:
Core:
- Improve runtime PM support, remove mmc_{suspend,resume}_host().
- Add MMC_CAP_RUNTIME_RESUME, for delaying MMC resume until we're
outside of the resume sequence (in runtime_resume) to decrease
system resume time.
Drivers:
- dw_mmc: Support HS200 mode.
- sdhci-eshdc-imx: Support SD3.0 SDR clock tuning, DDR on IMX6.
- sdhci-pci: Add support for Intel Clovertrail and Merrifield"
* tag 'mmc-updates-for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (108 commits)
mmc: wbsd: Silence compiler warning
mmc: core: Silence compiler warning in __mmc_switch
mmc: sh_mmcif: Convert to clk_prepare|unprepare
mmc: sh_mmcif: Convert to PM macros when defining dev_pm_ops
mmc: dw_mmc: exynos: Revert the sdr_timing assignment
mmc: sdhci: Avoid needless loop while handling SDIO interrupts in sdhci_irq
mmc: core: Add MMC_CAP_RUNTIME_RESUME to resume at runtime_resume
mmc: core: Improve runtime PM support during suspend/resume for sd/mmc
mmc: core: Remove redundant mmc_power_up|off at runtime callbacks
mmc: Don't force card to active state when entering suspend/shutdown
MIPS: db1235: Don't use MMC_CLKGATE
mmc: core: Remove deprecated mmc_suspend|resume_host APIs
mmc: mmci: Move away from using deprecated APIs
mmc: via-sdmmc: Move away from using deprecated APIs
mmc: tmio: Move away from using deprecated APIs
mmc: sh_mmcif: Move away from using deprecated APIs
mmc: sdricoh_cs: Move away from using deprecated APIs
mmc: rtsx: Remove redundant suspend and resume callbacks
mmc: wbsd: Move away from using deprecated APIs
mmc: pxamci: Remove redundant suspend and resume callbacks
...
Diffstat (limited to 'drivers')
45 files changed, 1786 insertions, 1231 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1a3163f1407e..29d5d988a51c 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2448,7 +2448,6 @@ static int _mmc_blk_suspend(struct mmc_card *card) 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); @@ -2483,7 +2482,6 @@ 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; } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 3e227bd91e81..64145a32b917 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -342,7 +342,7 @@ int mmc_add_card(struct mmc_card *card) break; } - if (mmc_sd_card_uhs(card) && + if (mmc_card_uhs(card) && (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index bf18b6bfce48..57a2b403bf8e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,6 +23,7 @@ #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> @@ -301,7 +302,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); + EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true); if (err) { pr_warn("%s: Error %d starting bkops\n", mmc_hostname(card->host), err); @@ -918,31 +919,6 @@ 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 * @@ -1382,22 +1358,31 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) { int bit; - ocr &= host->ocr_avail; + /* + * 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; + } - bit = ffs(ocr); - if (bit) { - bit -= 1; + 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; ocr &= 3 << bit; - - mmc_host_clk_hold(host); - host->ios.vdd = bit; - mmc_set_ios(host); - mmc_host_clk_release(host); + mmc_power_cycle(host, ocr); } else { - pr_warning("%s: host doesn't support card's voltages\n", - mmc_hostname(host)); - ocr = 0; + bit = fls(ocr) - 1; + ocr &= 3 << bit; + if (bit != host->ios.vdd) + dev_warn(mmc_dev(host), "exceeding card's volts\n"); } return ocr; @@ -1422,7 +1407,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) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) { struct mmc_command cmd = {0}; int err = 0; @@ -1504,7 +1489,7 @@ power_cycle: if (err) { pr_debug("%s: Signal voltage switch failed, " "power cycling card\n", mmc_hostname(host)); - mmc_power_cycle(host); + mmc_power_cycle(host, ocr); } mmc_host_clk_release(host); @@ -1545,22 +1530,14 @@ 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) +void mmc_power_up(struct mmc_host *host, u32 ocr) { - int bit; - if (host->ios.power_mode == MMC_POWER_ON) return; mmc_host_clk_hold(host); - /* 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; + host->ios.vdd = fls(ocr) - 1; if (mmc_host_is_spi(host)) host->ios.chip_select = MMC_CS_HIGH; else @@ -1604,13 +1581,6 @@ 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; @@ -1630,12 +1600,12 @@ void mmc_power_off(struct mmc_host *host) mmc_host_clk_release(host); } -void mmc_power_cycle(struct mmc_host *host) +void mmc_power_cycle(struct mmc_host *host, u32 ocr) { mmc_power_off(host); /* Wait at least 1 ms according to SD spec */ mmc_delay(1); - mmc_power_up(host); + mmc_power_up(host, ocr); } /* @@ -1723,6 +1693,28 @@ 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. @@ -1735,16 +1727,8 @@ void mmc_detach_bus(struct mmc_host *host) */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { -#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); + _mmc_detect_change(host, delay, true); } - EXPORT_SYMBOL(mmc_detect_change); void mmc_init_erase(struct mmc_card *card) @@ -2334,7 +2318,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); + mmc_power_up(host, host->ocr_avail); /* * Some eMMCs (with VCCQ always on) may not be reset after power up, so @@ -2423,7 +2407,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); + _mmc_detect_change(host, 0, false); } } @@ -2504,8 +2488,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); - mmc_detect_change(host, 0); + mmc_power_up(host, host->ocr_avail); + _mmc_detect_change(host, 0, false); } void mmc_stop_host(struct mmc_host *host) @@ -2583,7 +2567,7 @@ int mmc_power_restore_host(struct mmc_host *host) return -EINVAL; } - mmc_power_up(host); + mmc_power_up(host, host->card->ocr); ret = host->bus_ops->power_restore(host); mmc_bus_put(host); @@ -2657,28 +2641,6 @@ 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. @@ -2724,7 +2686,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); + _mmc_detect_change(host, 0, false); } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 5345d156493e..443a584660f0 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -42,13 +42,13 @@ void mmc_set_ungated(struct mmc_host *host); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); -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); 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_up(struct mmc_host *host, u32 ocr); void mmc_power_off(struct mmc_host *host); -void mmc_power_cycle(struct mmc_host *host); +void mmc_power_cycle(struct mmc_host *host, u32 ocr); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6d02012a1d0b..f631f5a9bf79 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -13,6 +13,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/pm_runtime.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -934,6 +935,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto err; } + card->ocr = ocr; card->type = MMC_TYPE_MMC; card->rca = 1; memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); @@ -1404,9 +1406,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) if (notify_type == EXT_CSD_POWER_OFF_LONG) timeout = card->ext_csd.power_off_longtime; - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_POWER_OFF_NOTIFICATION, - notify_type, timeout); + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + notify_type, timeout, true, false); if (err) pr_err("%s: Power Off Notification timed out, %u\n", mmc_hostname(card->host), timeout); @@ -1477,6 +1479,9 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) mmc_claim_host(host); + if (mmc_card_suspended(host->card)) + goto out; + if (mmc_card_doing_bkops(host->card)) { err = mmc_stop_bkops(host->card); if (err) @@ -1496,51 +1501,93 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) err = mmc_deselect_cards(host); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); - if (!err) + if (!err) { mmc_power_off(host); + mmc_card_set_suspended(host->card); + } out: mmc_release_host(host); return err; } /* - * Suspend callback from host. + * Suspend callback */ static int mmc_suspend(struct mmc_host *host) { - return _mmc_suspend(host, true); -} + int err; -/* - * Shutdown callback - */ -static int mmc_shutdown(struct mmc_host *host) -{ - return _mmc_suspend(host, false); + err = _mmc_suspend(host, true); + if (!err) { + pm_runtime_disable(&host->card->dev); + pm_runtime_set_suspended(&host->card->dev); + } + + return err; } /* - * Resume callback from host. - * * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ -static int mmc_resume(struct mmc_host *host) +static int _mmc_resume(struct mmc_host *host) { - int err; + int err = 0; BUG_ON(!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); + + if (!mmc_card_suspended(host->card)) + goto out; + + mmc_power_up(host, host->card->ocr); + err = mmc_init_card(host, host->card->ocr, host->card); + mmc_card_clr_suspended(host->card); + +out: mmc_release_host(host); + return err; +} + +/* + * Shutdown callback + */ +static int mmc_shutdown(struct mmc_host *host) +{ + int err = 0; + + /* + * In a specific case for poweroff notify, we need to resume the card + * before we can shutdown it properly. + */ + if (mmc_can_poweroff_notify(host->card) && + !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) + err = _mmc_resume(host); + + if (!err) + err = _mmc_suspend(host, false); return err; } +/* + * Callback for resume. + */ +static int mmc_resume(struct mmc_host *host) +{ + int err = 0; + + if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { + err = _mmc_resume(host); + pm_runtime_set_active(&host->card->dev); + pm_runtime_mark_last_busy(&host->card->dev); + } + pm_runtime_enable(&host->card->dev); + + return err; +} /* * Callback for runtime_suspend. @@ -1552,18 +1599,11 @@ static int mmc_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - mmc_claim_host(host); - - err = mmc_suspend(host); - if (err) { + err = _mmc_suspend(host, true); + 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; } @@ -1574,18 +1614,14 @@ static int mmc_runtime_resume(struct mmc_host *host) { int err; - if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) return 0; - mmc_claim_host(host); - - mmc_power_up(host); - err = mmc_resume(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; } @@ -1595,7 +1631,7 @@ static int mmc_power_restore(struct mmc_host *host) host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_claim_host(host); - ret = mmc_init_card(host, host->ocr, host->card); + ret = mmc_init_card(host, host->card->ocr, host->card); mmc_release_host(host); return ret; @@ -1640,7 +1676,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host) int mmc_attach_mmc(struct mmc_host *host) { int err; - u32 ocr; + u32 ocr, rocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1666,23 +1702,12 @@ int mmc_attach_mmc(struct mmc_host *host) goto err; } - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - pr_warning("%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - - host->ocr = mmc_select_voltage(host, ocr); + rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage of the card? */ - if (!host->ocr) { + if (!rocr) { err = -EINVAL; goto err; } @@ -1690,7 +1715,7 @@ int mmc_attach_mmc(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_init_card(host, host->ocr, NULL); + err = mmc_init_card(host, rocr, NULL); if (err) goto err; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index ef183483d5b6..e5b5eeb548d1 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,6 +23,40 @@ #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ +static inline int __mmc_send_status(struct mmc_card *card, u32 *status, + bool ignore_crc) +{ + int err; + struct mmc_command cmd = {0}; + + BUG_ON(!card); + BUG_ON(!card->host); + + cmd.opcode = MMC_SEND_STATUS; + if (!mmc_host_is_spi(card->host)) + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + if (ignore_crc) + cmd.flags &= ~MMC_RSP_CRC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + if (err) + return err; + + /* NOTE: callers are required to understand the difference + * between "native" and SPI format status words! + */ + if (status) + *status = cmd.resp[0]; + + return 0; +} + +int mmc_send_status(struct mmc_card *card, u32 *status) +{ + return __mmc_send_status(card, status, false); +} + static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; @@ -370,16 +404,18 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) * @timeout_ms: timeout (ms) for operation performed by register write, * timeout of zero implies maximum possible timeout * @use_busy_signal: use the busy signal as response type + * @send_status: send status cmd to poll for busy * * Modifies the EXT_CSD register for selected card. */ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, - unsigned int timeout_ms, bool use_busy_signal) + unsigned int timeout_ms, bool use_busy_signal, bool send_status) { int err; struct mmc_command cmd = {0}; unsigned long timeout; - u32 status; + u32 status = 0; + bool ignore_crc = false; BUG_ON(!card); BUG_ON(!card->host); @@ -408,17 +444,37 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, if (!use_busy_signal) return 0; - /* Must check status to be sure of no errors */ + /* + * Must check status to be sure of no errors + * If CMD13 is to check the busy completion of the timing change, + * disable the check of CRC error. + */ + if (index == EXT_CSD_HS_TIMING && + !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + ignore_crc = true; + timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); do { - err = mmc_send_status(card, &status); - if (err) - return err; + if (send_status) { + err = __mmc_send_status(card, &status, ignore_crc); + if (err) + return err; + } if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; + /* + * We are not allowed to issue a status command and the host + * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only + * rely on waiting for the stated timeout to be sufficient. + */ + if (!send_status) { + mmc_delay(timeout_ms); + return 0; + } + /* Timeout if the device never leaves the program state. */ if (time_after(jiffies, timeout)) { pr_err("%s: Card stuck in programming state! %s\n", @@ -445,36 +501,10 @@ EXPORT_SYMBOL_GPL(__mmc_switch); int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms) { - return __mmc_switch(card, set, index, value, timeout_ms, true); + return __mmc_switch(card, set, index, value, timeout_ms, true, true); } EXPORT_SYMBOL_GPL(mmc_switch); -int mmc_send_status(struct mmc_card *card, u32 *status) -{ - int err; - struct mmc_command cmd = {0}; - - BUG_ON(!card); - BUG_ON(!card->host); - - cmd.opcode = MMC_SEND_STATUS; - if (!mmc_host_is_spi(card->host)) - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); - if (err) - return err; - - /* NOTE: callers are required to understand the difference - * between "native" and SPI format status words! - */ - if (status) - *status = cmd.resp[0]; - - return 0; -} - static int mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5e8823dc3ef6..6f42050b7ccc 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -13,6 +13,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/pm_runtime.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -721,6 +722,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) int err; u32 max_current; int retries = 10; + u32 pocr = ocr; try_again: if (!retries) { @@ -773,7 +775,8 @@ try_again: */ if (!mmc_host_is_spi(host) && rocr && ((*rocr & 0x41000000) == 0x41000000)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, + pocr); if (err == -EAGAIN) { retries--; goto try_again; @@ -935,6 +938,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (IS_ERR(card)) return PTR_ERR(card); + card->ocr = ocr; card->type = MMC_TYPE_SD; memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } @@ -1064,10 +1068,7 @@ static void mmc_sd_detect(struct mmc_host *host) } } -/* - * Suspend callback from host. - */ -static int mmc_sd_suspend(struct mmc_host *host) +static int _mmc_sd_suspend(struct mmc_host *host) { int err = 0; @@ -1075,34 +1076,77 @@ static int mmc_sd_suspend(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); + + if (mmc_card_suspended(host->card)) + goto out; + if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~MMC_STATE_HIGHSPEED; - if (!err) + if (!err) { mmc_power_off(host); + mmc_card_set_suspended(host->card); + } + +out: mmc_release_host(host); + return err; +} + +/* + * Callback for suspend + */ +static int mmc_sd_suspend(struct mmc_host *host) +{ + int err; + + err = _mmc_sd_suspend(host); + if (!err) { + pm_runtime_disable(&host->card->dev); + pm_runtime_set_suspended(&host->card->dev); + } return err; } /* - * Resume callback from host. - * * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ -static int mmc_sd_resume(struct mmc_host *host) +static int _mmc_sd_resume(struct mmc_host *host) { - int err; + int err = 0; BUG_ON(!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); + + if (!mmc_card_suspended(host->card)) + goto out; + + mmc_power_up(host, host->card->ocr); + err = mmc_sd_init_card(host, host->card->ocr, host->card); + mmc_card_clr_suspended(host->card); + +out: mmc_release_host(host); + return err; +} + +/* + * Callback for resume + */ +static int mmc_sd_resume(struct mmc_host *host) +{ + int err = 0; + + if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { + err = _mmc_sd_resume(host); + pm_runtime_set_active(&host->card->dev); + pm_runtime_mark_last_busy(&host->card->dev); + } + pm_runtime_enable(&host->card->dev); return err; } @@ -1117,18 +1161,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - mmc_claim_host(host); - - err = mmc_sd_suspend(host); - if (err) { + 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; } @@ -1139,18 +1176,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) { int err; - if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) return 0; - mmc_claim_host(host); - - mmc_power_up(host); - err = mmc_sd_resume(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; } @@ -1160,7 +1193,7 @@ static int mmc_sd_power_restore(struct mmc_host *host) host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); - ret = mmc_sd_init_card(host, host->ocr, host->card); + ret = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_release_host(host); return ret; @@ -1205,7 +1238,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) int mmc_attach_sd(struct mmc_host *host) { int err; - u32 ocr; + u32 ocr, rocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1229,31 +1262,12 @@ int mmc_attach_sd(struct mmc_host *host) goto err; } - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - pr_warning("%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - - if ((ocr & MMC_VDD_165_195) && - !(host->ocr_avail_sd & MMC_VDD_165_195)) { - pr_warning("%s: SD card claims to support the " - "incompletely defined 'low voltage range'. This " - "will be ignored.\n", mmc_hostname(host)); - ocr &= ~MMC_VDD_165_195; - } - - host->ocr = mmc_select_voltage(host, ocr); + rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage(s) of the card(s)? */ - if (!host->ocr) { + if (!rocr) { err = -EINVAL; goto err; } @@ -1261,7 +1275,7 @@ int mmc_attach_sd(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_sd_init_card(host, host->ocr, NULL); + err = mmc_sd_init_card(host, rocr, NULL); if (err) goto err; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 80d89cff7306..4d721c6e2af0 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -593,23 +593,28 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *card; int err; int retries = 10; + u32 rocr = 0; + u32 ocr_card = ocr; BUG_ON(!host); WARN_ON(!host->claimed); + /* to query card if 1.8V signalling is supported */ + if (mmc_host_uhs(host)) + ocr |= R4_18V_PRESENT; + try_again: if (!retries) { pr_warning("%s: Skipping voltage switch\n", mmc_hostname(host)); ocr &= ~R4_18V_PRESENT; - host->ocr &= ~R4_18V_PRESENT; } /* * Inform the card of the voltage */ if (!powered_resume) { - err = mmc_send_io_op_cond(host, host->ocr, &ocr); + err = mmc_send_io_op_cond(host, ocr, &rocr); if (err) goto err; } @@ -632,8 +637,8 @@ try_again: goto err; } - if ((ocr & R4_MEMORY_PRESENT) && - mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) { + if ((rocr & R4_MEMORY_PRESENT) && + mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) { card->type = MMC_TYPE_SD_COMBO; if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || @@ -663,8 +668,9 @@ try_again: * systems that claim 1.8v signalling in fact do not support * it. */ - if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) { + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, + ocr); if (err == -EAGAIN) { sdio_reset(host); mmc_go_idle(host); @@ -674,12 +680,10 @@ try_again: goto try_again; } else if (err) { ocr &= ~R4_18V_PRESENT; - host->ocr &= ~R4_18V_PRESENT; } err = 0; } else { ocr &= ~R4_18V_PRESENT; - host->ocr &= ~R4_18V_PRESENT; } /* @@ -759,6 +763,7 @@ try_again: card = oldcard; } + card->ocr = ocr_card; mmc_fixup_device(card, NULL); if (card->type == MMC_TYPE_SD_COMBO) { @@ -981,8 +986,7 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Restore power if needed */ if (!mmc_card_keep_power(host)) { - mmc_power_up(host); - mmc_select_voltage(host, host->ocr); + mmc_power_up(host, host->card->ocr); /* * Tell runtime PM core we just powered up the card, * since it still believes the card is powered off. @@ -1000,7 +1004,7 @@ static int mmc_sdio_resume(struct mmc_host *host) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { sdio_reset(host); mmc_go_idle(host); - err = mmc_sdio_init_card(host, host->ocr, host->card, + err = mmc_sdio_init_card(host, host->card->ocr, host->card, mmc_card_keep_power(host)); } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ @@ -1040,7 +1044,6 @@ static int mmc_sdio_resume(struct mmc_host *host) static int mmc_sdio_power_restore(struct mmc_host *host) { int ret; - u32 ocr; BUG_ON(!host); BUG_ON(!host->card); @@ -1062,32 +1065,17 @@ static int mmc_sdio_power_restore(struct mmc_host *host) * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and * harmless in other situations. * - * With these steps taken, mmc_select_voltage() is also required to - * restore the correct voltage setting of the card. */ sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); - ret = mmc_send_io_op_cond(host, 0, &ocr); + ret = mmc_send_io_op_cond(host, 0, NULL); if (ret) goto out; - if (host->ocr_avail_sdio) - host->ocr_avail = host->ocr_avail_sdio; - - host->ocr = mmc_select_voltage(host, ocr & ~0x7F); - if (!host->ocr) { - ret = -EINVAL; - goto out; - } - - if (mmc_host_uhs(host)) - /* to query card if 1.8V signalling is supported */ - host->ocr |= R4_18V_PRESENT; - - ret = mmc_sdio_init_card(host, host->ocr, host->card, + ret = mmc_sdio_init_card(host, host->card->ocr, host->card, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); @@ -1108,7 +1096,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host) static int mmc_sdio_runtime_resume(struct mmc_host *host) { /* Restore power and re-initialize. */ - mmc_power_up(host); + mmc_power_up(host, host->card->ocr); return mmc_sdio_power_restore(host); } @@ -1131,7 +1119,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { int mmc_attach_sdio(struct mmc_host *host) { int err, i, funcs; - u32 ocr; + u32 ocr, rocr; struct mmc_card *card; BUG_ON(!host); @@ -1145,23 +1133,13 @@ int mmc_attach_sdio(struct mmc_host *host) if (host->ocr_avail_sdio) host->ocr_avail = host->ocr_avail_sdio; - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - pr_warning("%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - host->ocr = mmc_select_voltage(host, ocr); + rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage(s) of the card(s)? */ - if (!host->ocr) { + if (!rocr) { err = -EINVAL; goto err; } @@ -1169,22 +1147,10 @@ int mmc_attach_sdio(struct mmc_host *host) /* * Detect and init the card. */ - if (mmc_host_uhs(host)) - /* to query card if 1.8V signalling is supported */ - host->ocr |= R4_18V_PRESENT; + err = mmc_sdio_init_card(host, rocr, NULL, 0); + if (err) + goto err; - err = mmc_sdio_init_card(host, host->ocr, NULL, 0); - if (err) { - if (err == -EAGAIN) { - /* - * Retry initialization with S18R set to 0. - */ - host->ocr &= ~R4_18V_PRESENT; - err = mmc_sdio_init_card(host, host->ocr, NULL, 0); - } - if (err) - goto err; - } card = host->card; /* diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 69e438ee043e..2cbb4516d353 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -255,7 +255,6 @@ struct atmel_mci_slot { #define ATMCI_CARD_PRESENT 0 #define ATMCI_CARD_NEED_INIT 1 #define ATMCI_SHUTDOWN 2 -#define ATMCI_SUSPENDED 3 int detect_pin; int wp_pin; @@ -589,6 +588,13 @@ static void atmci_timeout_timer(unsigned long data) if (host->mrq->cmd->data) { host->mrq->cmd->data->error = -ETIMEDOUT; host->data = NULL; + /* + * With some SDIO modules, sometimes DMA transfer hangs. If + * stop_transfer() is not called then the DMA request is not + * removed, following ones are queued and never computed. + */ + if (host->state == STATE_DATA_XFER) + host->stop_transfer(host); } else { host->mrq->cmd->error = -ETIMEDOUT; host->cmd = NULL; @@ -1803,12 +1809,14 @@ static void atmci_tasklet_func(unsigned long priv) if (unlikely(status)) { host->stop_transfer(host); host->data = NULL; - if (status & ATMCI_DTOE) { - data->error = -ETIMEDOUT; - } else if (status & ATMCI_DCRCE) { - data->error = -EILSEQ; - } else { - data->error = -EIO; + if (data) { + if (status & ATMCI_DTOE) { + data->error = -ETIMEDOUT; + } else if (status & ATMCI_DCRCE) { + data->error = -EILSEQ; + } else { + data->error = -EIO; + } } } @@ -2520,70 +2528,10 @@ static int __exit atmci_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int atmci_suspend(struct device *dev) -{ - struct atmel_mci *host = dev_get_drvdata(dev); - int i; - - for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { - struct atmel_mci_slot *slot = host->slot[i]; - int ret; - - if (!slot) - continue; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slot[i]; - if (slot - && test_bit(ATMCI_SUSPENDED, &slot->flags)) { - mmc_resume_host(host->slot[i]->mmc); - clear_bit(ATMCI_SUSPENDED, &slot->flags); - } - } - return ret; - } else { - set_bit(ATMCI_SUSPENDED, &slot->flags); - } - } - - return 0; -} - -static int atmci_resume(struct device *dev) -{ - struct atmel_mci *host = dev_get_drvdata(dev); - int i; - int ret = 0; - - for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { - struct atmel_mci_slot *slot = host->slot[i]; - int err; - - slot = host->slot[i]; - if (!slot) - continue; - if (!test_bit(ATMCI_SUSPENDED, &slot->flags)) - continue; - err = mmc_resume_host(slot->mmc); - if (err < 0) - ret = err; - else - clear_bit(ATMCI_SUSPENDED, &slot->flags); - } - - return ret; -} -#endif - -static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); - static struct platform_driver atmci_driver = { .remove = __exit_p(atmci_remove), .driver = { .name = "atmel_mci", - .pm = &atmci_pm, .of_match_table = of_match_ptr(atmci_dt_ids), }, }; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index df9becdd2e99..f5443a6c4915 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1157,11 +1157,6 @@ static int au1xmmc_remove(struct platform_device *pdev) static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state) { struct au1xmmc_host *host = platform_get_drvdata(pdev); - int ret; - - ret = mmc_suspend_host(host->mmc); - if (ret) - return ret; au_writel(0, HOST_CONFIG2(host)); au_writel(0, HOST_CONFIG(host)); @@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev) au1xmmc_reset_controller(host); - return mmc_resume_host(host->mmc); + return 0; } #else #define au1xmmc_suspend NULL diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 94fae2f1baaf..2b7f37e82ca9 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Disable 4 bit SDIO */ cfg &= ~SD4E; } + bfin_write_SDH_CFG(cfg); host->power_mode = ios->power_mode; #ifndef RSI_BLKSZ @@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) cfg &= ~SD_CMD_OD; # endif - if (ios->power_mode != MMC_POWER_OFF) cfg |= PWR_ON; else @@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_ctl |= CLK_E; host->clk_div = clk_div; bfin_write_SDH_CLK_CTL(clk_ctl); - } else sdh_stop_clock(host); @@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int sdh_suspend(struct platform_device *dev, pm_message_t state) { - struct mmc_host *mmc = platform_get_drvdata(dev); struct bfin_sd_host *drv_data = get_sdh_data(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc); peripheral_free_list(drv_data->pin_req); - return ret; + return 0; } static int sdh_resume(struct platform_device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); struct bfin_sd_host *drv_data = get_sdh_data(dev); int ret = 0; @@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev) } sdh_reset(); - - if (mmc) - ret = mmc_resume_host(mmc); - return ret; } #else diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 9d6e2b844404..1087b4c79cd6 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -667,12 +667,6 @@ static const struct mmc_host_ops cb710_mmc_host = { static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); - struct mmc_host *mmc = cb710_slot_to_mmc(slot); - int err; - - err = mmc_suspend_host(mmc); - if (err) - return err; cb710_mmc_enable_irq(slot, 0, ~0); return 0; @@ -681,11 +675,9 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) static int cb710_mmc_resume(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); - struct mmc_host *mmc = cb710_slot_to_mmc(slot); cb710_mmc_enable_irq(slot, 0, ~0); - - return mmc_resume_host(mmc); + return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index e9fa87df909c..d6153740b77f 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -193,7 +193,6 @@ struct mmc_davinci_host { #define DAVINCI_MMC_DATADIR_READ 1 #define DAVINCI_MMC_DATADIR_WRITE 2 unsigned char data_dir; - unsigned char suspended; /* buffer is used during PIO of one scatterlist segment, and * is updated along with buffer_bytes_left. bytes_left applies @@ -1435,38 +1434,23 @@ static int davinci_mmcsd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mmc_davinci_host *host = platform_get_drvdata(pdev); - int ret; - ret = mmc_suspend_host(host->mmc); - if (!ret) { - writel(0, host->base + DAVINCI_MMCIM); - mmc_davinci_reset_ctrl(host, 1); - clk_disable(host->clk); - host->suspended = 1; - } else { - host->suspended = 0; - } + writel(0, host->base + DAVINCI_MMCIM); + mmc_davinci_reset_ctrl(host, 1); + clk_disable(host->clk); - return ret; + return 0; } static int davinci_mmcsd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mmc_davinci_host *host = platform_get_drvdata(pdev); - int ret; - - if (!host->suspended) - return 0; clk_enable(host->clk); - mmc_davinci_reset_ctrl(host, 0); - ret = mmc_resume_host(host->mmc); - if (!ret) - host->suspended = 0; - return ret; + return 0; } static const struct dev_pm_ops davinci_mmcsd_pm = { diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 6a1fa2110a05..3423c5ed50c7 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -14,8 +14,10 @@ #include <linux/clk.h> #include <linux/mmc/host.h> #include <linux/mmc/dw_mmc.h> +#include <linux/mmc/mmc.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/slab.h> #include "dw_mmc.h" #include "dw_mmc-pltfm.h" @@ -30,16 +32,39 @@ #define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ SDMMC_CLKSEL_CCLK_DRIVE(y) | \ SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_WAKEUP_INT BIT(11) #define EXYNOS4210_FIXED_CIU_CLK_DIV 2 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4 +/* Block number in eMMC */ +#define DWMCI_BLOCK_NUM 0xFFFFFFFF + +#define SDMMC_EMMCP_BASE 0x1000 +#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) +#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200) +#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204) +#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C) + +/* SMU control bits */ +#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7) +#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6) +#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5) +#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) +#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3) +#define DWMCI_MPSCTRL_ECB_MODE BIT(2) +#define DWMCI_MPSCTRL_ENCRYPTION BIT(1) +#define DWMCI_MPSCTRL_VALID BIT(0) + +#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */ + /* Variations in Exynos specific dw-mshc controller */ enum dw_mci_exynos_type { DW_MCI_TYPE_EXYNOS4210, DW_MCI_TYPE_EXYNOS4412, DW_MCI_TYPE_EXYNOS5250, DW_MCI_TYPE_EXYNOS5420, + DW_MCI_TYPE_EXYNOS5420_SMU, }; /* Exynos implementation specific driver private data */ @@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data { u8 ciu_div; u32 sdr_timing; u32 ddr_timing; + u32 cur_speed; }; static struct dw_mci_exynos_compatible { @@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible { }, { .compatible = "samsung,exynos5420-dw-mshc", .ctrl_type = DW_MCI_TYPE_EXYNOS5420, + }, { + .compatible = "samsung,exynos5420-dw-mshc-smu", + .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, }, }; static int dw_mci_exynos_priv_init(struct dw_mci *host) { - struct dw_mci_exynos_priv_data *priv; - int idx; - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); - return -ENOMEM; - } + struct dw_mci_exynos_priv_data *priv = host->priv; - for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { - if (of_device_is_compatible(host->dev->of_node, - exynos_compat[idx].compatible)) - priv->ctrl_type = exynos_compat[idx].ctrl_type; + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) { + mci_writel(host, MPSBEGIN0, 0); + mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); + mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | + DWMCI_MPSCTRL_NON_SECURE_READ_BIT | + DWMCI_MPSCTRL_VALID | + DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT); } - host->priv = priv; return 0; } static int dw_mci_exynos_setup_clock(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned long rate = clk_get_rate(host->ciu_clk); - if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420) - host->bus_hz /= (priv->ciu_div + 1); - else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) - host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV; - else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) - host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV; + host->bus_hz = rate / (priv->ciu_div + 1); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_exynos_suspend(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + return dw_mci_suspend(host); +} + +static int dw_mci_exynos_resume(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + dw_mci_exynos_priv_init(host); + return dw_mci_resume(host); +} + +/** + * dw_mci_exynos_resume_noirq - Exynos-specific resume code + * + * On exynos5420 there is a silicon errata that will sometimes leave the + * WAKEUP_INT bit in the CLKSEL register asserted. This bit is 1 to indicate + * that it fired and we can clear it by writing a 1 back. Clear it to prevent + * interrupts from going off constantly. + * + * We run this code on all exynos variants because it doesn't hurt. + */ + +static int dw_mci_exynos_resume_noirq(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + u32 clksel; + + clksel = mci_readl(host, CLKSEL); + if (clksel & SDMMC_CLKSEL_WAKEUP_INT) + mci_writel(host, CLKSEL, clksel); return 0; } +#else +#define dw_mci_exynos_suspend NULL +#define dw_mci_exynos_resume NULL +#define dw_mci_exynos_resume_noirq NULL +#endif /* CONFIG_PM_SLEEP */ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) { @@ -121,23 +183,68 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned int wanted = ios->clock; + unsigned long actual; + u8 div = priv->ciu_div + 1; - if (ios->timing == MMC_TIMING_UHS_DDR50) + if (ios->timing == MMC_TIMING_UHS_DDR50) { mci_writel(host, CLKSEL, priv->ddr_timing); - else + /* Should be double rate for DDR mode */ + if (ios->bus_width == MMC_BUS_WIDTH_8) + wanted <<= 1; + } else { mci_writel(host, CLKSEL, priv->sdr_timing); + } + + /* Don't care if wanted clock is zero */ + if (!wanted) + return; + + /* Guaranteed minimum frequency for cclkin */ + if (wanted < EXYNOS_CCLKIN_MIN) + wanted = EXYNOS_CCLKIN_MIN; + + if (wanted != priv->cur_speed) { + int ret = clk_set_rate(host->ciu_clk, wanted * div); + if (ret) + dev_warn(host->dev, + "failed to set clk-rate %u error: %d\n", + wanted * div, ret); + actual = clk_get_rate(host->ciu_clk); + host->bus_hz = actual / div; + priv->cur_speed = wanted; + host->current_speed = 0; + } } static int dw_mci_exynos_parse_dt(struct dw_mci *host) { - struct dw_mci_exynos_priv_data *priv = host->priv; + struct dw_mci_exynos_priv_data *priv; struct device_node *np = host->dev->of_node; u32 timing[2]; u32 div = 0; + int idx; int ret; - of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); - priv->ciu_div = div; + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(host->dev, "mem alloc failed for private data\n"); + return -ENOMEM; + } + + for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { + if (of_device_is_compatible(np, exynos_compat[idx].compatible)) + priv->ctrl_type = exynos_compat[idx].ctrl_type; + } + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) + priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1; + else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) + priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1; + else { + of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); + priv->ciu_div = div; + } ret = of_property_read_u32_array(np, "samsung,dw-mshc-sdr-timing", timing, 2); @@ -152,9 +259,131 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) return ret; priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); + host->priv = priv; return 0; } +static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) +{ + return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); +} + +static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) +{ + u32 clksel; + clksel = mci_readl(host, CLKSEL); + clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); + mci_writel(host, CLKSEL, clksel); +} + +static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) +{ + u32 clksel; + u8 sample; + + clksel = mci_readl(host, CLKSEL); + sample = (clksel + 1) & 0x7; + clksel = (clksel & ~0x7) | sample; + mci_writel(host, CLKSEL, clksel); + return sample; +} + +static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) +{ + const u8 iter = 8; + u8 __c; + s8 i, loc = -1; + + for (i = 0; i < iter; i++) { + __c = ror8(candiates, i); + if ((__c & 0xc7) == 0xc7) { + loc = i; + goto out; + } + } + + for (i = 0; i < iter; i++) { + __c = ror8(candiates, i); + if ((__c & 0x83) == 0x83) { + loc = i; + goto out; + } + } + +out: + return loc; +} + +static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, + struct dw_mci_tuning_data *tuning_data) +{ + struct dw_mci *host = slot->host; + struct mmc_host *mmc = slot->mmc; + const u8 *blk_pattern = tuning_data->blk_pattern; + u8 *blk_test; + unsigned int blksz = tuning_data->blksz; + u8 start_smpl, smpl, candiates = 0; + s8 found = -1; + int ret = 0; + + blk_test = kmalloc(blksz, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + start_smpl = dw_mci_exynos_get_clksmpl(host); + + do { + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_command stop = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + + data.blksz = blksz; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, blk_test, blksz); + mrq.cmd = &cmd; + mrq.stop = &stop; + mrq.data = &data; + host->mrq = &mrq; + + mci_writel(host, TMOUT, ~0); + smpl = dw_mci_exynos_move_next_clksmpl(host); + + mmc_wait_for_req(mmc, &mrq); + + if (!cmd.error && !data.error) { + if (!memcmp(blk_pattern, blk_test, blksz)) + candiates |= (1 << smpl); + } else { + dev_dbg(host->dev, + "Tuning error: cmd.error:%d, data.error:%d\n", + cmd.error, data.error); + } + } while (start_smpl != smpl); + + found = dw_mci_exynos_get_best_clksmpl(candiates); + if (found >= 0) + dw_mci_exynos_set_clksmpl(host, found); + else + ret = -EIO; + + kfree(blk_test); + return ret; +} + /* Common capabilities of Exynos4/Exynos5 SoC */ static unsigned long exynos_dwmmc_caps[4] = { MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | @@ -171,6 +400,7 @@ static const struct dw_mci_drv_data exynos_drv_data = { .prepare_command = dw_mci_exynos_prepare_command, .set_ios = dw_mci_exynos_set_ios, .parse_dt = dw_mci_exynos_parse_dt, + .execute_tuning = dw_mci_exynos_execute_tuning, }; static const struct of_device_id dw_mci_exynos_match[] = { @@ -180,6 +410,8 @@ static const struct of_device_id dw_mci_exynos_match[] = { .data = &exynos_drv_data, }, { .compatible = "samsung,exynos5420-dw-mshc", .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos5420-dw-mshc-smu", + .data = &exynos_drv_data, }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); @@ -194,13 +426,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, drv_data); } +const struct dev_pm_ops dw_mci_exynos_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume) + .resume_noirq = dw_mci_exynos_resume_noirq, + .thaw_noirq = dw_mci_exynos_resume_noirq, + .restore_noirq = dw_mci_exynos_resume_noirq, +}; + static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, .remove = __exit_p(dw_mci_pltfm_remove), .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, - .pm = &dw_mci_pltfm_pmops, + .pm = &dw_mci_exynos_pmops, }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 20897529ea5e..5c4965655297 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -39,7 +39,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, { struct dw_mci *host; struct resource *regs; - int ret; host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); if (!host) @@ -59,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, if (IS_ERR(host->regs)) return PTR_ERR(host->regs); - if (drv_data && drv_data->init) { - ret = drv_data->init(host); - if (ret) - return ret; - } - platform_set_drvdata(pdev, host); return dw_mci_probe(host); } diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c index 14b5961a851c..3e8e53ae3302 100644 --- a/drivers/mmc/host/dw_mmc-socfpga.c +++ b/drivers/mmc/host/dw_mmc-socfpga.c @@ -38,21 +38,6 @@ struct dw_mci_socfpga_priv_data { static int dw_mci_socfpga_priv_init(struct dw_mci *host) { - struct dw_mci_socfpga_priv_data *priv; - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); - return -ENOMEM; - } - - priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); - if (IS_ERR(priv->sysreg)) { - dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n"); - return PTR_ERR(priv->sysreg); - } - host->priv = priv; - return 0; } @@ -79,12 +64,24 @@ static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr) static int dw_mci_socfpga_parse_dt(struct dw_mci *host) { - struct dw_mci_socfpga_priv_data *priv = host->priv; + struct dw_mci_socfpga_priv_data *priv; struct device_node *np = host->dev->of_node; u32 timing[2]; u32 div = 0; int ret; + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(host->dev, "mem alloc failed for private data\n"); + return -ENOMEM; + } + + priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(priv->sysreg)) { + dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n"); + return PTR_ERR(priv->sysreg); + } + ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div); if (ret) dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1"); @@ -96,6 +93,7 @@ static int dw_mci_socfpga_parse_dt(struct dw_mci *host) return ret; priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]); + host->priv = priv; return 0; } @@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = { }; MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match); -int dw_mci_socfpga_probe(struct platform_device *pdev) +static int dw_mci_socfpga_probe(struct platform_device *pdev) { const struct dw_mci_drv_data *drv_data; const struct of_device_id *match; @@ -128,7 +126,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = { .remove = __exit_p(dw_mci_pltfm_remove), .driver = { .name = "dwmmc_socfpga", - .of_match_table = of_match_ptr(dw_mci_socfpga_match), + .of_match_table = dw_mci_socfpga_match, .pm = &dw_mci_pltfm_pmops, }, }; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 018f365e5ae4..4bce0deec362 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,6 +29,7 @@ #include <linux/irq.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> +#include <linux/mmc/sdio.h> #include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> @@ -50,6 +51,9 @@ #define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 +#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ +#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */ + #ifdef CONFIG_MMC_DW_IDMAC #define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ @@ -76,42 +80,39 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ -/** - * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. - * @host: The MMC controller this slot is using. - * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) - * @wp_gpio: If gpio_is_valid() we'll use this to read write protect. - * @ctype: Card type for this slot. - * @mrq: mmc_request currently being processed or waiting to be - * processed, or NULL when the slot is idle. - * @queue_node: List node for placing this node in the @queue list of - * &struct dw_mci. - * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @flags: Random state bits associated with the slot. - * @id: Number of this slot. - * @last_detect_state: Most recently observed card detect state. - */ -struct dw_mci_slot { - struct mmc_host *mmc; - struct dw_mci *host; - - int quirks; - int wp_gpio; - - u32 ctype; - - struct mmc_request *mrq; - struct list_head queue_node; +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; - unsigned int clock; - unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 - int id; - int last_detect_state; +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; +static inline bool dw_mci_fifo_reset(struct dw_mci *host); +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host); + #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) { @@ -249,10 +250,15 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr = cmd->opcode; - if (cmdr == MMC_STOP_TRANSMISSION) + if (cmd->opcode == MMC_STOP_TRANSMISSION || + cmd->opcode == MMC_GO_IDLE_STATE || + cmd->opcode == MMC_GO_INACTIVE_STATE || + (cmd->opcode == SD_IO_RW_DIRECT && + ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) cmdr |= SDMMC_CMD_STOP; else - cmdr |= SDMMC_CMD_PRV_DAT_WAIT; + if (cmd->opcode != MMC_SEND_STATUS && cmd->data) + cmdr |= SDMMC_CMD_PRV_DAT_WAIT; if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ @@ -279,6 +285,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) return cmdr; } +static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) +{ + struct mmc_command *stop; + u32 cmdr; + + if (!cmd->data) + return 0; + + stop = &host->stop_abort; + cmdr = cmd->opcode; + memset(stop, 0, sizeof(struct mmc_command)); + + if (cmdr == MMC_READ_SINGLE_BLOCK || + cmdr == MMC_READ_MULTIPLE_BLOCK || + cmdr == MMC_WRITE_BLOCK || + cmdr == MMC_WRITE_MULTIPLE_BLOCK) { + stop->opcode = MMC_STOP_TRANSMISSION; + stop->arg = 0; + stop->flags = MMC_RSP_R1B | MMC_CMD_AC; + } else if (cmdr == SD_IO_RW_EXTENDED) { + stop->opcode = SD_IO_RW_DIRECT; + stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | + ((cmd->arg >> 28) & 0x7); + stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; + } else { + return 0; + } + + cmdr = stop->opcode | SDMMC_CMD_STOP | + SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; + + return cmdr; +} + static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { @@ -293,9 +333,10 @@ static void dw_mci_start_command(struct dw_mci *host, mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); } -static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) +static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) { - dw_mci_start_command(host, data->stop, host->stop_cmdr); + struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; + dw_mci_start_command(host, stop, host->stop_cmdr); } /* DMA interface functions */ @@ -304,10 +345,10 @@ static void dw_mci_stop_dma(struct dw_mci *host) if (host->using_dma) { host->dma_ops->stop(host); host->dma_ops->cleanup(host); - } else { - /* Data transfer was stopped by the interrupt handler */ - set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } + + /* Data transfer was stopped by the interrupt handler */ + set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } static int dw_mci_get_dma_dir(struct mmc_data *data) @@ -331,6 +372,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) dw_mci_get_dma_dir(data)); } +static void dw_mci_idmac_reset(struct dw_mci *host) +{ + u32 bmod = mci_readl(host, BMOD); + /* Software reset of DMA */ + bmod |= SDMMC_IDMAC_SWRESET; + mci_writel(host, BMOD, bmod); +} + static void dw_mci_idmac_stop_dma(struct dw_mci *host) { u32 temp; @@ -344,6 +393,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host) /* Stop the IDMAC running */ temp = mci_readl(host, BMOD); temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB); + temp |= SDMMC_IDMAC_SWRESET; mci_writel(host, BMOD, temp); } @@ -435,7 +485,7 @@ static int dw_mci_idmac_init(struct dw_mci *host) p->des3 = host->sg_dma; p->des0 = IDMAC_DES0_ER; - mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); + dw_mci_idmac_reset(host); /* Mask out interrupts - get Tx & Rx complete only */ mci_writel(host, IDSTS, IDMAC_INT_CLR); @@ -532,6 +582,78 @@ static void dw_mci_post_req(struct mmc_host *mmc, data->host_cookie = 0; } +static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) +{ +#ifdef CONFIG_MMC_DW_IDMAC + unsigned int blksz = data->blksz; + const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; + u32 fifo_width = 1 << host->data_shift; + u32 blksz_depth = blksz / fifo_width, fifoth_val; + u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; + int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; + + tx_wmark = (host->fifo_depth) / 2; + tx_wmark_invers = host->fifo_depth - tx_wmark; + + /* + * MSIZE is '1', + * if blksz is not a multiple of the FIFO width + */ + if (blksz % fifo_width) { + msize = 0; + rx_wmark = 1; + goto done; + } + + do { + if (!((blksz_depth % mszs[idx]) || + (tx_wmark_invers % mszs[idx]))) { + msize = idx; + rx_wmark = mszs[idx] - 1; + break; + } + } while (--idx > 0); + /* + * If idx is '0', it won't be tried + * Thus, initial values are uesed + */ +done: + fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark); + mci_writel(host, FIFOTH, fifoth_val); +#endif +} + +static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) +{ + unsigned int blksz = data->blksz; + u32 blksz_depth, fifo_depth; + u16 thld_size; + + WARN_ON(!(data->flags & MMC_DATA_READ)); + + if (host->timing != MMC_TIMING_MMC_HS200 && + host->timing != MMC_TIMING_UHS_SDR104) + goto disable; + + blksz_depth = blksz / (1 << host->data_shift); + fifo_depth = host->fifo_depth; + + if (blksz_depth > fifo_depth) + goto disable; + + /* + * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz' + * If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz + * Currently just choose blksz. + */ + thld_size = blksz; + mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1)); + return; + +disable: + mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0)); +} + static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) { int sg_len; @@ -556,6 +678,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, sg_len); + /* + * Decide the MSIZE and RX/TX Watermark. + * If current block size is same with previous size, + * no need to update fifoth. + */ + if (host->prev_blksz != data->blksz) + dw_mci_adjust_fifoth(host, data); + /* Enable the DMA interface */ temp = mci_readl(host, CTRL); temp |= SDMMC_CTRL_DMA_ENABLE; @@ -581,10 +711,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->sg = NULL; host->data = data; - if (data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) { host->dir_status = DW_MCI_RECV_STATUS; - else + dw_mci_ctrl_rd_thld(host, data); + } else { host->dir_status = DW_MCI_SEND_STATUS; + } if (dw_mci_submit_data_dma(host, data)) { int flags = SG_MITER_ATOMIC; @@ -606,6 +738,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) temp = mci_readl(host, CTRL); temp &= ~SDMMC_CTRL_DMA_ENABLE; mci_writel(host, CTRL, temp); + + /* + * Use the initial fifoth_val for PIO mode. + * If next issued data may be transfered by DMA mode, + * prev_blksz should be invalidated. + */ + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + } else { + /* + * Keep the current block size. + * It will be used to decide whether to update + * fifoth register next time. + */ + host->prev_blksz = data->blksz; } } @@ -632,24 +779,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot->host; + unsigned int clock = slot->clock; u32 div; u32 clk_en_a; - if (slot->clock != host->current_speed || force_clkinit) { - div = host->bus_hz / slot->clock; - if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) + if (!clock) { + mci_writel(host, CLKENA, 0); + mci_send_cmd(slot, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + } else if (clock != host->current_speed || force_clkinit) { + div = host->bus_hz / clock; + if (host->bus_hz % clock && host->bus_hz > clock) /* * move the + 1 after the divide to prevent * over-clocking the card. */ div += 1; - div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0; + div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" - " div = %d)\n", slot->id, host->bus_hz, slot->clock, - div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); + if ((clock << div) != slot->__clk_old || force_clkinit) + dev_info(&slot->mmc->class_dev, + "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", + slot->id, host->bus_hz, clock, + div ? ((host->bus_hz / div) >> 1) : + host->bus_hz, div); /* disable clock */ mci_writel(host, CLKENA, 0); @@ -676,9 +830,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); - host->current_speed = slot->clock; + /* keep the clock with reflecting clock dividor */ + slot->__clk_old = clock << div; } + host->current_speed = clock; + /* Set the current slot bus width */ mci_writel(host, CTYPE, (slot->ctype << slot->id)); } @@ -700,7 +857,9 @@ static void __dw_mci_start_request(struct dw_mci *host, host->pending_events = 0; host->completed_events = 0; + host->cmd_status = 0; host->data_status = 0; + host->dir_status = 0; data = cmd->data; if (data) { @@ -724,6 +883,8 @@ static void __dw_mci_start_request(struct dw_mci *host, if (mrq->stop) host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); + else + host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } static void dw_mci_start_request(struct dw_mci *host, @@ -806,14 +967,13 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) regs &= ~((0x1 << slot->id) << 16); mci_writel(slot->host, UHS_REG, regs); + slot->host->timing = ios->timing; - if (ios->clock) { - /* - * Use mirror of ios->clock to prevent race with mmc - * core ios update when finding the minimum. - */ - slot->clock = ios->clock; - } + /* + * Use mirror of ios->clock to prevent race with mmc + * core ios update when finding the minimum. + */ + slot->clock = ios->clock; if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); @@ -939,6 +1099,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) } } +static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct dw_mci_tuning_data tuning_data; + int err = -ENOSYS; + + if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + tuning_data.blk_pattern = tuning_blk_pattern_8bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + return -EINVAL; + } + } else if (opcode == MMC_SEND_TUNING_BLOCK) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + dev_err(host->dev, + "Undefined command(%d) for tuning\n", opcode); + return -EINVAL; + } + + if (drv_data && drv_data->execute_tuning) + err = drv_data->execute_tuning(slot, opcode, &tuning_data); + return err; +} + static const struct mmc_host_ops dw_mci_ops = { .request = dw_mci_request, .pre_req = dw_mci_pre_req, @@ -947,6 +1139,7 @@ static const struct mmc_host_ops dw_mci_ops = { .get_ro = dw_mci_get_ro, .get_cd = dw_mci_get_cd, .enable_sdio_irq = dw_mci_enable_sdio_irq, + .execute_tuning = dw_mci_execute_tuning, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -978,7 +1171,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) spin_lock(&host->lock); } -static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) +static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) { u32 status = host->cmd_status; @@ -1012,12 +1205,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd /* newer ip versions need a delay between retries */ if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) mdelay(20); + } - if (cmd->data) { - dw_mci_stop_dma(host); - host->data = NULL; + return cmd->error; +} + +static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) +{ + u32 status = host->data_status; + + if (status & DW_MCI_DATA_ERROR_FLAGS) { + if (status & SDMMC_INT_DRTO) { + data->error = -ETIMEDOUT; + } else if (status & SDMMC_INT_DCRC) { + data->error = -EILSEQ; + } else if (status & SDMMC_INT_EBE) { + if (host->dir_status == + DW_MCI_SEND_STATUS) { + /* + * No data CRC status was returned. + * The number of bytes transferred + * will be exaggerated in PIO mode. + */ + data->bytes_xfered = 0; + data->error = -ETIMEDOUT; + } else if (host->dir_status == + DW_MCI_RECV_STATUS) { + data->error = -EIO; + } + } else { + /* SDMMC_INT_SBE is included */ + data->error = -EIO; } + + dev_err(host->dev, "data error, status 0x%08x\n", status); + + /* + * After an error, there may be data lingering + * in the FIFO + */ + dw_mci_fifo_reset(host); + } else { + data->bytes_xfered = data->blocks * data->blksz; + data->error = 0; } + + return data->error; } static void dw_mci_tasklet_func(unsigned long priv) @@ -1025,14 +1258,16 @@ static void dw_mci_tasklet_func(unsigned long priv) struct dw_mci *host = (struct dw_mci *)priv; struct mmc_data *data; struct mmc_command *cmd; + struct mmc_request *mrq; enum dw_mci_state state; enum dw_mci_state prev_state; - u32 status, ctrl; + unsigned int err; spin_lock(&host->lock); state = host->state; data = host->data; + mrq = host->mrq; do { prev_state = state; @@ -1049,16 +1284,23 @@ static void dw_mci_tasklet_func(unsigned long priv) cmd = host->cmd; host->cmd = NULL; set_bit(EVENT_CMD_COMPLETE, &host->completed_events); - dw_mci_command_complete(host, cmd); - if (cmd == host->mrq->sbc && !cmd->error) { + err = dw_mci_command_complete(host, cmd); + if (cmd == mrq->sbc && !err) { prev_state = state = STATE_SENDING_CMD; __dw_mci_start_request(host, host->cur_slot, - host->mrq->cmd); + mrq->cmd); goto unlock; } - if (!host->mrq->data || cmd->error) { - dw_mci_request_end(host, host->mrq); + if (cmd->data && err) { + dw_mci_stop_dma(host); + send_stop_abort(host, data); + state = STATE_SENDING_STOP; + break; + } + + if (!cmd->data || err) { + dw_mci_request_end(host, mrq); goto unlock; } @@ -1069,8 +1311,7 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); - if (data->stop) - send_stop_cmd(host, data); + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -1090,60 +1331,27 @@ static void dw_mci_tasklet_func(unsigned long priv) host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); - status = host->data_status; - - if (status & DW_MCI_DATA_ERROR_FLAGS) { - if (status & SDMMC_INT_DRTO) { - data->error = -ETIMEDOUT; - } else if (status & SDMMC_INT_DCRC) { - data->error = -EILSEQ; - } else if (status & SDMMC_INT_EBE && - host->dir_status == - DW_MCI_SEND_STATUS) { - /* - * No data CRC status was returned. - * The number of bytes transferred will - * be exaggerated in PIO mode. - */ - data->bytes_xfered = 0; - data->error = -ETIMEDOUT; - } else { - dev_err(host->dev, - "data FIFO error " - "(status=%08x)\n", - status); - data->error = -EIO; - } - /* - * After an error, there may be data lingering - * in the FIFO, so reset it - doing so - * generates a block interrupt, hence setting - * the scatter-gather pointer to NULL. - */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - } else { - data->bytes_xfered = data->blocks * data->blksz; - data->error = 0; - } + err = dw_mci_data_complete(host, data); - if (!data->stop) { - dw_mci_request_end(host, host->mrq); - goto unlock; - } + if (!err) { + if (!data->stop || mrq->sbc) { + if (mrq->sbc) + data->stop->error = 0; + dw_mci_request_end(host, mrq); + goto unlock; + } - if (host->mrq->sbc && !data->error) { - data->stop->error = 0; - dw_mci_request_end(host, host->mrq); - goto unlock; + /* stop command for open-ended transfer*/ + if (data->stop) + send_stop_abort(host, data); } + /* + * If err has non-zero, + * stop-abort command has been already issued. + */ prev_state = state = STATE_SENDING_STOP; - if (!data->error) - send_stop_cmd(host, data); + /* fall through */ case STATE_SENDING_STOP: @@ -1151,9 +1359,19 @@ static void dw_mci_tasklet_func(unsigned long priv) &host->pending_events)) break; + /* CMD error in data command */ + if (mrq->cmd->error && mrq->data) + dw_mci_fifo_reset(host); + host->cmd = NULL; - dw_mci_command_complete(host, host->mrq->stop); - dw_mci_request_end(host, host->mrq); + host->data = NULL; + + if (mrq->stop) + dw_mci_command_complete(host, mrq->stop); + else + host->cmd_status = 0; + + dw_mci_request_end(host, mrq); goto unlock; case STATE_DATA_ERROR: @@ -1697,7 +1915,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) struct mmc_host *mmc = slot->mmc; struct mmc_request *mrq; int present; - u32 ctrl; present = dw_mci_get_cd(mmc); while (present != slot->last_detect_state) { @@ -1736,11 +1953,10 @@ static void dw_mci_work_routine_card(struct work_struct *work) case STATE_DATA_ERROR: if (mrq->data->error == -EINPROGRESS) mrq->data->error = -ENOMEDIUM; - if (!mrq->stop) - break; /* fall through */ case STATE_SENDING_STOP: - mrq->stop->error = -ENOMEDIUM; + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; break; } @@ -1763,23 +1979,10 @@ static void dw_mci_work_routine_card(struct work_struct *work) if (present == 0) { clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* - * Clear down the FIFO - doing so generates a - * block interrupt, hence setting the - * scatter-gather pointer to NULL. - */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - + /* Clear down the FIFO */ + dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC - ctrl = mci_readl(host, BMOD); - /* Software reset of DMA */ - ctrl |= SDMMC_IDMAC_SWRESET; - mci_writel(host, BMOD, ctrl); + dw_mci_idmac_reset(host); #endif } @@ -1901,6 +2104,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) struct dw_mci_slot *slot; const struct dw_mci_drv_data *drv_data = host->drv_data; int ctrl_id, ret; + u32 freq[2]; u8 bus_width; mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); @@ -1916,8 +2120,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id); mmc->ops = &dw_mci_ops; - mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); - mmc->f_max = host->bus_hz; + if (of_property_read_u32_array(host->dev->of_node, + "clock-freq-min-max", freq, 2)) { + mmc->f_min = DW_MCI_FREQ_MIN; + mmc->f_max = DW_MCI_FREQ_MAX; + } else { + mmc->f_min = freq[0]; + mmc->f_max = freq[1]; + } if (host->pdata->get_ocr) mmc->ocr_avail = host->pdata->get_ocr(id); @@ -1964,9 +2174,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->caps |= MMC_CAP_4_BIT_DATA; } - if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) - mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; - if (host->pdata->blk_settings) { mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; @@ -2008,12 +2215,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) /* Card initially undetected */ slot->last_detect_state = 0; - /* - * Card may have been plugged in prior to boot so we - * need to run the detect tasklet - */ - queue_work(host->card_workqueue, &host->card_work); - return 0; err_setup_bus: @@ -2074,36 +2275,57 @@ no_dma: return; } -static bool mci_wait_reset(struct device *dev, struct dw_mci *host) +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) { unsigned long timeout = jiffies + msecs_to_jiffies(500); - unsigned int ctrl; + u32 ctrl; - mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET)); + ctrl = mci_readl(host, CTRL); + ctrl |= reset; + mci_writel(host, CTRL, ctrl); /* wait till resets clear */ do { ctrl = mci_readl(host, CTRL); - if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET))) + if (!(ctrl & reset)) return true; } while (time_before(jiffies, timeout)); - dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + dev_err(host->dev, + "Timeout resetting block (ctrl reset %#x)\n", + ctrl & reset); return false; } +static inline bool dw_mci_fifo_reset(struct dw_mci *host) +{ + /* + * Reseting generates a block interrupt, hence setting + * the scatter-gather pointer to NULL. + */ + if (host->sg) { + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + } + + return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET); +} + +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host) +{ + return dw_mci_ctrl_reset(host, + SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_RESET | + SDMMC_CTRL_DMA_RESET); +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; int id; } of_quirks[] = { { - .quirk = "supports-highspeed", - .id = DW_MCI_QUIRK_HIGHSPEED, - }, { .quirk = "broken-cd", .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, }, @@ -2158,6 +2380,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (of_find_property(np, "enable-sdio-wakeup", NULL)) pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + if (of_find_property(np, "supports-highspeed", NULL)) + pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR; + + if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; + return pdata; } @@ -2221,6 +2452,15 @@ int dw_mci_probe(struct dw_mci *host) host->bus_hz = clk_get_rate(host->ciu_clk); } + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) { + dev_err(host->dev, + "implementation specific init failed\n"); + goto err_clk_ciu; + } + } + if (drv_data && drv_data->setup_clock) { ret = drv_data->setup_clock(host); if (ret) { @@ -2287,7 +2527,7 @@ int dw_mci_probe(struct dw_mci *host) } /* Reset all blocks */ - if (!mci_wait_reset(host->dev, host)) + if (!dw_mci_ctrl_all_reset(host)) return -ENODEV; host->dma_ops = host->pdata->dma_ops; @@ -2317,8 +2557,8 @@ int dw_mci_probe(struct dw_mci *host) fifo_size = host->pdata->fifo_depth; } host->fifo_depth = fifo_size; - host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | - ((fifo_size/2) << 0)); + host->fifoth_val = + SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2); mci_writel(host, FIFOTH, host->fifoth_val); /* disable clock to CIU */ @@ -2456,23 +2696,6 @@ EXPORT_SYMBOL(dw_mci_remove); */ int dw_mci_suspend(struct dw_mci *host) { - int i, ret = 0; - - for (i = 0; i < host->num_slots; i++) { - struct dw_mci_slot *slot = host->slot[i]; - if (!slot) - continue; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slot[i]; - if (slot) - mmc_resume_host(host->slot[i]->mmc); - } - return ret; - } - } - if (host->vmmc) regulator_disable(host->vmmc); @@ -2493,7 +2716,7 @@ int dw_mci_resume(struct dw_mci *host) } } - if (!mci_wait_reset(host->dev, host)) { + if (!dw_mci_ctrl_all_reset(host)) { ret = -ENODEV; return ret; } @@ -2501,8 +2724,15 @@ int dw_mci_resume(struct dw_mci *host) if (host->use_dma && host->dma_ops->init) host->dma_ops->init(host); - /* Restore the old value at FIFOTH register */ + /* + * Restore the initial value at FIFOTH register + * And Invalidate the prev_blksz with zero + */ mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + + /* Put in max timeout */ + mci_writel(host, TMOUT, 0xFFFFFFFF); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | @@ -2518,10 +2748,6 @@ int dw_mci_resume(struct dw_mci *host) dw_mci_set_ios(slot->mmc, &slot->mmc->ios); dw_mci_setup_bus(slot, true); } - - ret = mmc_resume_host(host->slot[i]->mmc); - if (ret < 0) - return ret; } return 0; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 81b29941c5b9..6bf24ab917e6 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -53,6 +53,7 @@ #define SDMMC_IDINTEN 0x090 #define SDMMC_DSCADDR 0x094 #define SDMMC_BUFADDR 0x098 +#define SDMMC_CDTHRCTL 0x100 #define SDMMC_DATA(x) (x) /* @@ -128,6 +129,10 @@ #define SDMMC_CMD_INDX(n) ((n) & 0x1F) /* Status register defines */ #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) +/* FIFOTH register defines */ +#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ + ((r) & 0xFFF) << 16 | \ + ((t) & 0xFFF)) /* Internal DMAC interrupt defines */ #define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_NI BIT(8) @@ -142,6 +147,8 @@ #define SDMMC_IDMAC_SWRESET BIT(0) /* Version ID register define */ #define SDMMC_GET_VERID(x) ((x) & 0xFFFF) +/* Card read threshold */ +#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) /* Register access macros */ #define mci_readl(dev, reg) \ @@ -184,6 +191,52 @@ extern int dw_mci_resume(struct dw_mci *host); #endif /** + * struct dw_mci_slot - MMC slot state + * @mmc: The mmc_host representing this slot. + * @host: The MMC controller this slot is using. + * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) + * @wp_gpio: If gpio_is_valid() we'll use this to read write protect. + * @ctype: Card type for this slot. + * @mrq: mmc_request currently being processed or waiting to be + * processed, or NULL when the slot is idle. + * @queue_node: List node for placing this node in the @queue list of + * &struct dw_mci. + * @clock: Clock rate configured by set_ios(). Protected by host->lock. + * @__clk_old: The last updated clock with reflecting clock divider. + * Keeping track of this helps us to avoid spamming the console + * with CONFIG_MMC_CLKGATE. + * @flags: Random state bits associated with the slot. + * @id: Number of this slot. + * @last_detect_state: Most recently observed card detect state. + */ +struct dw_mci_slot { + struct mmc_host *mmc; + struct dw_mci *host; + + int quirks; + int wp_gpio; + + u32 ctype; + + struct mmc_request *mrq; + struct list_head queue_node; + + unsigned int clock; + unsigned int __clk_old; + + unsigned long flags; +#define DW_MMC_CARD_PRESENT 0 +#define DW_MMC_CARD_NEED_INIT 1 + int id; + int last_detect_state; +}; + +struct dw_mci_tuning_data { + const u8 *blk_pattern; + unsigned int blksz; +}; + +/** * dw_mci driver data - dw-mshc implementation specific driver data. * @caps: mmc subsystem specified capabilities of the controller(s). * @init: early implementation specific initialization. @@ -203,5 +256,7 @@ struct dw_mci_drv_data { void (*prepare_command)(struct dw_mci *host, u32 *cmdr); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); + int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode, + struct dw_mci_tuning_data *tuning_data); }; #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 66516339e3a0..de2139cf3444 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -880,8 +880,6 @@ static int jz4740_mmc_suspend(struct device *dev) { struct jz4740_mmc_host *host = dev_get_drvdata(dev); - mmc_suspend_host(host->mmc); - jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); return 0; @@ -893,8 +891,6 @@ static int jz4740_mmc_resume(struct device *dev) jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); - mmc_resume_host(host->mmc); - return 0; } diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index d135c76c4855..f32057972dd7 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1730,37 +1730,28 @@ static int mmci_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct mmc_host *mmc = amba_get_drvdata(adev); - int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); - - ret = mmc_suspend_host(mmc); - if (ret == 0) { - pm_runtime_get_sync(dev); - writel(0, host->base + MMCIMASK0); - } + pm_runtime_get_sync(dev); + writel(0, host->base + MMCIMASK0); } - return ret; + return 0; } static int mmci_resume(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct mmc_host *mmc = amba_get_drvdata(adev); - int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); - writel(MCI_IRQENABLE, host->base + MMCIMASK0); pm_runtime_put(dev); - - ret = mmc_resume_host(mmc); } - return ret; + return 0; } #endif diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b900de4e7e94..9405ecdaf6cf 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1416,28 +1416,10 @@ ioremap_free: } #ifdef CONFIG_PM -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ -static void -do_resume_work(struct work_struct *work) -{ - struct msmsdcc_host *host = - container_of(work, struct msmsdcc_host, resume_task); - struct mmc_host *mmc = host->mmc; - - if (mmc) { - mmc_resume_host(mmc); - if (host->stat_irq) - enable_irq(host->stat_irq); - } -} -#endif - - static int msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { struct mmc_host *mmc = mmc_get_drvdata(dev); - int rc = 0; if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); @@ -1445,14 +1427,11 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (host->stat_irq) disable_irq(host->stat_irq); - if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) - rc = mmc_suspend_host(mmc); - if (!rc) - msmsdcc_writel(host, 0, MMCIMASK0); + msmsdcc_writel(host, 0, MMCIMASK0); if (host->clks_on) msmsdcc_disable_clocks(host, 0); } - return rc; + return 0; } static int @@ -1467,8 +1446,6 @@ msmsdcc_resume(struct platform_device *dev) msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); - if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) - mmc_resume_host(mmc); if (host->stat_irq) enable_irq(host->stat_irq); #if BUSCLK_PWRSAVE diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index deecee08c288..45aa2206741d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -775,9 +775,9 @@ static int mvsd_probe(struct platform_device *pdev) spin_lock_init(&host->lock); - host->base = devm_request_and_ioremap(&pdev->dev, r); - if (!host->base) { - ret = -ENOMEM; + host->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); goto out; } @@ -838,33 +838,6 @@ static int mvsd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int mvsd_suspend(struct platform_device *dev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc); - - return ret; -} - -static int mvsd_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} -#else -#define mvsd_suspend NULL -#define mvsd_resume NULL -#endif - static const struct of_device_id mvsdio_dt_ids[] = { { .compatible = "marvell,orion-sdio" }, { /* sentinel */ } @@ -874,8 +847,6 @@ MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); static struct platform_driver mvsd_driver = { .probe = mvsd_probe, .remove = mvsd_remove, - .suspend = mvsd_suspend, - .resume = mvsd_resume, .driver = { .name = DRIVER_NAME, .of_match_table = mvsdio_dt_ids, diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index c174c6a0d224..f7199c83f5cf 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1250,28 +1250,20 @@ static int mxcmci_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); - int ret = 0; - if (mmc) - ret = mmc_suspend_host(mmc); clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - - return ret; + return 0; } static int mxcmci_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); - int ret = 0; clk_prepare_enable(host->clk_per); clk_prepare_enable(host->clk_ipg); - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; + return 0; } static const struct dev_pm_ops mxcmci_pm_ops = { diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e1fa3ef735e0..50fc9df791b2 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -724,13 +724,9 @@ static int mxs_mmc_suspend(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - int ret = 0; - - ret = mmc_suspend_host(mmc); clk_disable_unprepare(ssp->clk); - - return ret; + return 0; } static int mxs_mmc_resume(struct device *dev) @@ -738,13 +734,9 @@ static int mxs_mmc_resume(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - int ret = 0; clk_prepare_enable(ssp->clk); - - ret = mmc_resume_host(mmc); - - return ret; + return 0; } static const struct dev_pm_ops mxs_mmc_pm_ops = { diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index b94f38ec2a83..0b10a9030f4e 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -128,7 +128,6 @@ struct mmc_omap_slot { struct mmc_omap_host { int initialized; - int suspended; struct mmc_request * mrq; struct mmc_command * cmd; struct mmc_data * data; @@ -1513,61 +1512,9 @@ static int mmc_omap_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - int i, ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - if (host == NULL || host->suspended) - return 0; - - for (i = 0; i < host->nr_slots; i++) { - struct mmc_omap_slot *slot; - - slot = host->slots[i]; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slots[i]; - mmc_resume_host(slot->mmc); - } - return ret; - } - } - host->suspended = 1; - return 0; -} - -static int mmc_omap_resume(struct platform_device *pdev) -{ - int i, ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - if (host == NULL || !host->suspended) - return 0; - - for (i = 0; i < host->nr_slots; i++) { - struct mmc_omap_slot *slot; - slot = host->slots[i]; - ret = mmc_resume_host(slot->mmc); - if (ret < 0) - return ret; - - host->suspended = 0; - } - return 0; -} -#else -#define mmc_omap_suspend NULL -#define mmc_omap_resume NULL -#endif - static struct platform_driver mmc_omap_driver = { .probe = mmc_omap_probe, .remove = mmc_omap_remove, - .suspend = mmc_omap_suspend, - .resume = mmc_omap_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 6ac63df645c4..dbd32ad3b749 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -75,6 +75,7 @@ #define ICE 0x1 #define ICS 0x2 #define CEN (1 << 2) +#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */ #define CLKD_MASK 0x0000FFC0 #define CLKD_SHIFT 6 #define DTO_MASK 0x000F0000 @@ -119,7 +120,8 @@ BRR_EN | BWR_EN | TC_EN | CC_EN) #define MMC_AUTOSUSPEND_DELAY 100 -#define MMC_TIMEOUT_MS 20 +#define MMC_TIMEOUT_MS 20 /* 20 mSec */ +#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */ #define OMAP_MMC_MIN_CLOCK 400000 #define OMAP_MMC_MAX_CLOCK 52000000 #define DRIVER_NAME "omap_hsmmc" @@ -171,6 +173,10 @@ struct omap_hsmmc_host { unsigned char bus_mode; unsigned char power_mode; int suspended; + u32 con; + u32 hctl; + u32 sysctl; + u32 capa; int irq; int use_dma, dma_ch; struct dma_chan *tx_chan; @@ -183,7 +189,6 @@ struct omap_hsmmc_host { int use_reg; int req_in_progress; struct omap_hsmmc_next next_data; - struct omap_mmc_platform_data *pdata; }; @@ -493,8 +498,8 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios) if (ios->clock) { dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock); - if (dsor > 250) - dsor = 250; + if (dsor > CLKD_MAX) + dsor = CLKD_MAX; } return dsor; @@ -597,25 +602,20 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host) static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) { struct mmc_ios *ios = &host->mmc->ios; - struct omap_mmc_platform_data *pdata = host->pdata; - int context_loss = 0; u32 hctl, capa; unsigned long timeout; - if (pdata->get_context_loss_count) { - context_loss = pdata->get_context_loss_count(host->dev); - if (context_loss < 0) - return 1; - } - - dev_dbg(mmc_dev(host->mmc), "context was %slost\n", - context_loss == host->context_loss ? "not " : ""); - if (host->context_loss == context_loss) - return 1; - if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) return 1; + if (host->con == OMAP_HSMMC_READ(host->base, CON) && + host->hctl == OMAP_HSMMC_READ(host->base, HCTL) && + host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) && + host->capa == OMAP_HSMMC_READ(host->base, CAPA)) + return 0; + + host->context_loss++; + if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { if (host->power_mode != MMC_POWER_OFF && (1 << ios->vdd) <= MMC_VDD_23_24) @@ -655,9 +655,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) omap_hsmmc_set_bus_mode(host); out: - host->context_loss = context_loss; - - dev_dbg(mmc_dev(host->mmc), "context is restored\n"); + dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n", + host->context_loss); return 0; } @@ -666,15 +665,10 @@ out: */ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) { - struct omap_mmc_platform_data *pdata = host->pdata; - int context_loss; - - if (pdata->get_context_loss_count) { - context_loss = pdata->get_context_loss_count(host->dev); - if (context_loss < 0) - return; - host->context_loss = context_loss; - } + host->con = OMAP_HSMMC_READ(host->base, CON); + host->hctl = OMAP_HSMMC_READ(host->base, HCTL); + host->sysctl = OMAP_HSMMC_READ(host->base, SYSCTL); + host->capa = OMAP_HSMMC_READ(host->base, CAPA); } #else @@ -975,8 +969,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, unsigned long bit) { unsigned long i = 0; - unsigned long limit = (loops_per_jiffy * - msecs_to_jiffies(MMC_TIMEOUT_MS)); + unsigned long limit = MMC_TIMEOUT_US; OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | bit); @@ -988,13 +981,13 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) && (i++ < limit)) - cpu_relax(); + udelay(1); } i = 0; while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && (i++ < limit)) - cpu_relax(); + udelay(1); if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) dev_err(mmc_dev(host->mmc), @@ -1178,9 +1171,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) struct omap_mmc_slot_data *slot = &mmc_slot(host); int carddetect; - if (host->suspended) - return IRQ_HANDLED; - sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (slot->card_detect) @@ -1635,18 +1625,9 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) { struct mmc_host *mmc = s->private; struct omap_hsmmc_host *host = mmc_priv(mmc); - int context_loss = 0; - - if (host->pdata->get_context_loss_count) - context_loss = host->pdata->get_context_loss_count(host->dev); - seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n", - mmc->index, host->context_loss, context_loss); - - if (host->suspended) { - seq_printf(s, "host suspended, can't read registers\n"); - return 0; - } + seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", + mmc->index, host->context_loss); pm_runtime_get_sync(host->dev); @@ -1838,13 +1819,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->ops = &omap_hsmmc_ops; - /* - * If regulator_disable can only put vcc_aux to sleep then there is - * no off state. - */ - if (mmc_slot(host).vcc_aux_disable_is_sleep) - mmc_slot(host).no_off = 1; - mmc->f_min = OMAP_MMC_MIN_CLOCK; if (pdata->max_freq > 0) @@ -1874,7 +1848,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_context_save(host); /* This can be removed once we support PBIAS with DT */ - if (host->dev->of_node && host->mapbase == 0x4809c000) + if (host->dev->of_node && res->start == 0x4809c000) host->pbias_disable = 1; host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); @@ -2119,23 +2093,12 @@ static void omap_hsmmc_complete(struct device *dev) static int omap_hsmmc_suspend(struct device *dev) { - int ret = 0; struct omap_hsmmc_host *host = dev_get_drvdata(dev); if (!host) return 0; - if (host && host->suspended) - return 0; - pm_runtime_get_sync(host->dev); - host->suspended = 1; - ret = mmc_suspend_host(host->mmc); - - if (ret) { - host->suspended = 0; - goto err; - } if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { omap_hsmmc_disable_irq(host); @@ -2145,23 +2108,19 @@ static int omap_hsmmc_suspend(struct device *dev) if (host->dbclk) clk_disable_unprepare(host->dbclk); -err: + pm_runtime_put_sync(host->dev); - return ret; + return 0; } /* Routine to resume the MMC device */ static int omap_hsmmc_resume(struct device *dev) { - int ret = 0; struct omap_hsmmc_host *host = dev_get_drvdata(dev); if (!host) return 0; - if (host && !host->suspended) - return 0; - pm_runtime_get_sync(host->dev); if (host->dbclk) @@ -2172,16 +2131,9 @@ static int omap_hsmmc_resume(struct device *dev) omap_hsmmc_protect_card(host); - /* Notify the core to resume the host */ - ret = mmc_resume_host(host->mmc); - if (ret == 0) - host->suspended = 0; - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); - - return ret; - + return 0; } #else diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1956a3df7cf3..32fe11323f39 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -880,35 +880,6 @@ static int pxamci_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int pxamci_suspend(struct device *dev) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc); - - return ret; -} - -static int pxamci_resume(struct device *dev) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} - -static const struct dev_pm_ops pxamci_pm_ops = { - .suspend = pxamci_suspend, - .resume = pxamci_resume, -}; -#endif - static struct platform_driver pxamci_driver = { .probe = pxamci_probe, .remove = pxamci_remove, @@ -916,9 +887,6 @@ static struct platform_driver pxamci_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(pxa_mmc_dt_ids), -#ifdef CONFIG_PM - .pm = &pxamci_pm_ops, -#endif }, }; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 375a880e0c5f..c46feda07d56 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -364,7 +364,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) struct mmc_host *mmc = host->mmc; struct mmc_card *card = mmc->card; struct mmc_data *data = mrq->data; - int uhs = mmc_sd_card_uhs(card); + int uhs = mmc_card_uhs(card); int read = (data->flags & MMC_DATA_READ) ? 1 : 0; u8 cfg2, trans_mode; int err; @@ -1197,37 +1197,6 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = { .execute_tuning = sdmmc_execute_tuning, }; -#ifdef CONFIG_PM -static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - struct mmc_host *mmc = host->mmc; - int err; - - dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); - - err = mmc_suspend_host(mmc); - if (err) - return err; - - return 0; -} - -static int rtsx_pci_sdmmc_resume(struct platform_device *pdev) -{ - struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - struct mmc_host *mmc = host->mmc; - - dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); - - return mmc_resume_host(mmc); -} -#else /* CONFIG_PM */ -#define rtsx_pci_sdmmc_suspend NULL -#define rtsx_pci_sdmmc_resume NULL -#endif /* CONFIG_PM */ - static void init_extra_caps(struct realtek_pci_sdmmc *host) { struct mmc_host *mmc = host->mmc; @@ -1367,8 +1336,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = { .probe = rtsx_pci_sdmmc_drv_probe, .remove = rtsx_pci_sdmmc_drv_remove, .id_table = rtsx_pci_sdmmc_ids, - .suspend = rtsx_pci_sdmmc_suspend, - .resume = rtsx_pci_sdmmc_resume, .driver = { .owner = THIS_MODULE, .name = DRV_NAME_RTSX_PCI_SDMMC, diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 8d6794cdf899..2fce5ea5eb39 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1949,39 +1949,10 @@ static struct platform_device_id s3cmci_driver_ids[] = { MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); - -#ifdef CONFIG_PM - -static int s3cmci_suspend(struct device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - - return mmc_suspend_host(mmc); -} - -static int s3cmci_resume(struct device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - - return mmc_resume_host(mmc); -} - -static const struct dev_pm_ops s3cmci_pm = { - .suspend = s3cmci_suspend, - .resume = s3cmci_resume, -}; - -#define s3cmci_pm_ops &s3cmci_pm -#else /* CONFIG_PM */ -#define s3cmci_pm_ops NULL -#endif /* CONFIG_PM */ - - static struct platform_driver s3cmci_driver = { .driver = { .name = "s3c-sdi", .owner = THIS_MODULE, - .pm = s3cmci_pm_ops, }, .id_table = s3cmci_driver_ids, .probe = s3cmci_probe, diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 85472d3fd37f..7a190fe4dff1 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -316,19 +316,7 @@ err_pltfm_free: static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev) { - struct sdhci_host *host = platform_get_drvdata(pdev); - int dead; - u32 scratch; - - dead = 0; - scratch = readl(host->ioaddr + SDHCI_INT_STATUS); - if (scratch == (u32)-1) - dead = 1; - sdhci_remove_host(host, dead); - - sdhci_free_host(host); - - return 0; + return sdhci_pltfm_unregister(pdev); } static struct platform_driver sdhci_bcm_kona_driver = { diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 36fa2df04660..f6d8d67c545f 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -178,13 +178,7 @@ err: static int bcm2835_sdhci_remove(struct platform_device *pdev) { - struct sdhci_host *host = platform_get_drvdata(pdev); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - - sdhci_remove_host(host, dead); - sdhci_pltfm_free(pdev); - - return 0; + return sdhci_pltfm_unregister(pdev); } static const struct of_device_id bcm2835_sdhci_of_match[] = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index abc8cf01e6e3..461a4c3f4ef7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -34,12 +34,40 @@ /* VENDOR SPEC register */ #define ESDHC_VENDOR_SPEC 0xc0 #define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) +#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1) +#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) #define ESDHC_WTMK_LVL 0x44 #define ESDHC_MIX_CTRL 0x48 +#define ESDHC_MIX_CTRL_DDREN (1 << 3) #define ESDHC_MIX_CTRL_AC23EN (1 << 7) +#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22) +#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23) +#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) /* Bits 3 and 6 are not SDHCI standard definitions */ #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 +/* dll control register */ +#define ESDHC_DLL_CTRL 0x60 +#define ESDHC_DLL_OVERRIDE_VAL_SHIFT 9 +#define ESDHC_DLL_OVERRIDE_EN_SHIFT 8 + +/* tune control register */ +#define ESDHC_TUNE_CTRL_STATUS 0x68 +#define ESDHC_TUNE_CTRL_STEP 1 +#define ESDHC_TUNE_CTRL_MIN 0 +#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1) + +#define ESDHC_TUNING_CTRL 0xcc +#define ESDHC_STD_TUNING_EN (1 << 24) +/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ +#define ESDHC_TUNING_START_TAP 0x1 + +#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64 + +/* pinctrl state */ +#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" +#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz" + /* * Our interpretation of the SDHCI_HOST_CONTROL register */ @@ -66,21 +94,60 @@ * As a result, the TC flag is not asserted and SW received timeout * exeception. Bit1 of Vendor Spec registor is used to fix it. */ -#define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1) - -enum imx_esdhc_type { - IMX25_ESDHC, - IMX35_ESDHC, - IMX51_ESDHC, - IMX53_ESDHC, - IMX6Q_USDHC, +#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1) +/* + * The flag enables the workaround for ESDHC errata ENGcm07207 which + * affects i.MX25 and i.MX35. + */ +#define ESDHC_FLAG_ENGCM07207 BIT(2) +/* + * The flag tells that the ESDHC controller is an USDHC block that is + * integrated on the i.MX6 series. + */ +#define ESDHC_FLAG_USDHC BIT(3) +/* The IP supports manual tuning process */ +#define ESDHC_FLAG_MAN_TUNING BIT(4) +/* The IP supports standard tuning process */ +#define ESDHC_FLAG_STD_TUNING BIT(5) +/* The IP has SDHCI_CAPABILITIES_1 register */ +#define ESDHC_FLAG_HAVE_CAP1 BIT(6) + +struct esdhc_soc_data { + u32 flags; +}; + +static struct esdhc_soc_data esdhc_imx25_data = { + .flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx35_data = { + .flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx51_data = { + .flags = 0, +}; + +static struct esdhc_soc_data esdhc_imx53_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, +}; + +static struct esdhc_soc_data usdhc_imx6q_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING, +}; + +static struct esdhc_soc_data usdhc_imx6sl_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1, }; struct pltfm_imx_data { - int flags; u32 scratchpad; - enum imx_esdhc_type devtype; struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_100mhz; + struct pinctrl_state *pins_200mhz; + const struct esdhc_soc_data *socdata; struct esdhc_platform_data boarddata; struct clk *clk_ipg; struct clk *clk_ahb; @@ -90,25 +157,20 @@ struct pltfm_imx_data { MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ } multiblock_status; - + u32 uhs_mode; + u32 is_ddr; }; static struct platform_device_id imx_esdhc_devtype[] = { { .name = "sdhci-esdhc-imx25", - .driver_data = IMX25_ESDHC, + .driver_data = (kernel_ulong_t) &esdhc_imx25_data, }, { .name = "sdhci-esdhc-imx35", - .driver_data = IMX35_ESDHC, + .driver_data = (kernel_ulong_t) &esdhc_imx35_data, }, { .name = "sdhci-esdhc-imx51", - .driver_data = IMX51_ESDHC, - }, { - .name = "sdhci-esdhc-imx53", - .driver_data = IMX53_ESDHC, - }, { - .name = "sdhci-usdhc-imx6q", - .driver_data = IMX6Q_USDHC, + .driver_data = (kernel_ulong_t) &esdhc_imx51_data, }, { /* sentinel */ } @@ -116,38 +178,34 @@ static struct platform_device_id imx_esdhc_devtype[] = { MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype); static const struct of_device_id imx_esdhc_dt_ids[] = { - { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], }, - { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], }, - { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], }, - { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], }, - { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], }, + { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, }, + { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, }, + { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, }, + { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, }, + { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, + { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); static inline int is_imx25_esdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX25_ESDHC; -} - -static inline int is_imx35_esdhc(struct pltfm_imx_data *data) -{ - return data->devtype == IMX35_ESDHC; + return data->socdata == &esdhc_imx25_data; } -static inline int is_imx51_esdhc(struct pltfm_imx_data *data) +static inline int is_imx53_esdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX51_ESDHC; + return data->socdata == &esdhc_imx53_data; } -static inline int is_imx53_esdhc(struct pltfm_imx_data *data) +static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX53_ESDHC; + return data->socdata == &usdhc_imx6q_data; } -static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) +static inline int esdhc_is_usdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX6Q_USDHC; + return !!(data->socdata->flags & ESDHC_FLAG_USDHC); } static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) @@ -164,7 +222,21 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) struct pltfm_imx_data *imx_data = pltfm_host->priv; u32 val = readl(host->ioaddr + reg); + if (unlikely(reg == SDHCI_PRESENT_STATE)) { + u32 fsl_prss = val; + /* save the least 20 bits */ + val = fsl_prss & 0x000FFFFF; + /* move dat[0-3] bits */ + val |= (fsl_prss & 0x0F000000) >> 4; + /* move cmd line bit */ + val |= (fsl_prss & 0x00800000) << 1; + } + if (unlikely(reg == SDHCI_CAPABILITIES)) { + /* ignore bit[0-15] as it stores cap_1 register val for mx6sl */ + if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1) + val &= 0xffff0000; + /* In FSL esdhc IC module, only bit20 is used to indicate the * ADMA2 capability of esdhc, but this bit is messed up on * some SOCs (e.g. on MX25, MX35 this bit is set, but they @@ -178,6 +250,25 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) } } + if (unlikely(reg == SDHCI_CAPABILITIES_1)) { + if (esdhc_is_usdhc(imx_data)) { + if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1) + val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF; + else + /* imx6q/dl does not have cap_1 register, fake one */ + val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 + | SDHCI_SUPPORT_SDR50 + | SDHCI_USE_SDR50_TUNING; + } + } + + if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) { + val = 0; + val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT; + val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT; + val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT; + } + if (unlikely(reg == SDHCI_INT_STATUS)) { if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) { val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR; @@ -224,7 +315,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) } } - if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) + if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (reg == SDHCI_INT_STATUS) && (val & SDHCI_INT_DATA_END))) { u32 v; @@ -256,10 +347,12 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; + u16 ret = 0; + u32 val; if (unlikely(reg == SDHCI_HOST_VERSION)) { reg ^= 2; - if (is_imx6q_usdhc(imx_data)) { + if (esdhc_is_usdhc(imx_data)) { /* * The usdhc register returns a wrong host version. * Correct it here. @@ -268,6 +361,30 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) } } + if (unlikely(reg == SDHCI_HOST_CONTROL2)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + if (val & ESDHC_VENDOR_SPEC_VSELECT) + ret |= SDHCI_CTRL_VDD_180; + + if (esdhc_is_usdhc(imx_data)) { + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) + val = readl(host->ioaddr + ESDHC_MIX_CTRL); + else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) + /* the std tuning bits is in ACMD12_ERR for imx6sl */ + val = readl(host->ioaddr + SDHCI_ACMD12_ERR); + } + + if (val & ESDHC_MIX_CTRL_EXE_TUNE) + ret |= SDHCI_CTRL_EXEC_TUNING; + if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) + ret |= SDHCI_CTRL_TUNED_CLK; + + ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK); + ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + + return ret; + } + return readw(host->ioaddr + reg); } @@ -275,10 +392,59 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; + u32 new_val = 0; switch (reg) { + case SDHCI_CLOCK_CONTROL: + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + if (val & SDHCI_CLOCK_CARD_EN) + new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; + else + new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); + return; + case SDHCI_HOST_CONTROL2: + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + if (val & SDHCI_CTRL_VDD_180) + new_val |= ESDHC_VENDOR_SPEC_VSELECT; + else + new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); + imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { + new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); + if (val & SDHCI_CTRL_TUNED_CLK) + new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL; + else + new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + writel(new_val , host->ioaddr + ESDHC_MIX_CTRL); + } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { + u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); + u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); + new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL); + if (val & SDHCI_CTRL_EXEC_TUNING) { + new_val |= ESDHC_STD_TUNING_EN | + ESDHC_TUNING_START_TAP; + v |= ESDHC_MIX_CTRL_EXE_TUNE; + m |= ESDHC_MIX_CTRL_FBCLK_SEL; + } else { + new_val &= ~ESDHC_STD_TUNING_EN; + v &= ~ESDHC_MIX_CTRL_EXE_TUNE; + m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + } + + if (val & SDHCI_CTRL_TUNED_CLK) + v |= ESDHC_MIX_CTRL_SMPCLK_SEL; + else + v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + + writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL); + writel(v, host->ioaddr + SDHCI_ACMD12_ERR); + writel(m, host->ioaddr + ESDHC_MIX_CTRL); + } + return; case SDHCI_TRANSFER_MODE: - if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) + if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (host->cmd->opcode == SD_IO_RW_EXTENDED) && (host->cmd->data->blocks > 1) && (host->cmd->data->flags & MMC_DATA_READ)) { @@ -288,7 +454,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(v, host->ioaddr + ESDHC_VENDOR_SPEC); } - if (is_imx6q_usdhc(imx_data)) { + if (esdhc_is_usdhc(imx_data)) { u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); /* Swap AC23 bit */ if (val & SDHCI_TRNS_AUTO_CMD23) { @@ -310,10 +476,10 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) val |= SDHCI_CMD_ABORTCMD; if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) && - (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) + (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) imx_data->multiblock_status = MULTIBLK_IN_PROCESS; - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) writel(val << 16, host->ioaddr + SDHCI_TRANSFER_MODE); else @@ -379,8 +545,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) * The reset on usdhc fails to clear MIX_CTRL register. * Do it manually here. */ - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) { writel(0, host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 0; + } } } @@ -409,8 +577,60 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + unsigned int host_clock = clk_get_rate(pltfm_host->clk); + int pre_div = 2; + int div = 1; + u32 temp, val; + + if (clock == 0) { + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } + goto out; + } + + if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) + pre_div = 1; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | ESDHC_CLOCK_MASK); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + while (host_clock / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; + + while (host_clock / pre_div / div > clock && div < 16) + div++; - esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk)); + host->mmc->actual_clock = host_clock / pre_div / div; + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->mmc->actual_clock); + + if (imx_data->is_ddr) + pre_div >>= 2; + else + pre_div >>= 1; + div--; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | (div << ESDHC_DIVIDER_SHIFT) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } + + mdelay(1); +out: + host->clock = clock; } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -454,7 +674,192 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) return 0; } -static const struct sdhci_ops sdhci_esdhc_ops = { +static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) +{ + u32 reg; + + /* FIXME: delay a bit for card to be ready for next tuning due to errors */ + mdelay(1); + + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | + ESDHC_MIX_CTRL_FBCLK_SEL; + writel(reg, host->ioaddr + ESDHC_MIX_CTRL); + writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + dev_dbg(mmc_dev(host->mmc), + "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n", + val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS)); +} + +static void esdhc_request_done(struct mmc_request *mrq) +{ + complete(&mrq->completion); +} + +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) +{ + struct mmc_command cmd = {0}; + struct mmc_request mrq = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN]; + + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern)); + + mrq.cmd = &cmd; + mrq.cmd->mrq = &mrq; + mrq.data = &data; + mrq.data->mrq = &mrq; + mrq.cmd->data = mrq.data; + + mrq.done = esdhc_request_done; + init_completion(&(mrq.completion)); + + disable_irq(host->irq); + spin_lock(&host->lock); + host->mrq = &mrq; + + sdhci_send_command(host, mrq.cmd); + + spin_unlock(&host->lock); + enable_irq(host->irq); + + wait_for_completion(&mrq.completion); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + return 0; +} + +static void esdhc_post_tuning(struct sdhci_host *host) +{ + u32 reg; + + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg &= ~ESDHC_MIX_CTRL_EXE_TUNE; + writel(reg, host->ioaddr + ESDHC_MIX_CTRL); +} + +static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) +{ + int min, max, avg, ret; + + /* find the mininum delay first which can pass tuning */ + min = ESDHC_TUNE_CTRL_MIN; + while (min < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, min); + if (!esdhc_send_tuning_cmd(host, opcode)) + break; + min += ESDHC_TUNE_CTRL_STEP; + } + + /* find the maxinum delay which can not pass tuning */ + max = min + ESDHC_TUNE_CTRL_STEP; + while (max < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, max); + if (esdhc_send_tuning_cmd(host, opcode)) { + max -= ESDHC_TUNE_CTRL_STEP; + break; + } + max += ESDHC_TUNE_CTRL_STEP; + } + + /* use average delay to get the best timing */ + avg = (min + max) / 2; + esdhc_prepare_tuning(host, avg); + ret = esdhc_send_tuning_cmd(host, opcode); + esdhc_post_tuning(host); + + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", + ret ? "failed" : "passed", avg, ret); + + return ret; +} + +static int esdhc_change_pinstate(struct sdhci_host *host, + unsigned int uhs) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + struct pinctrl_state *pinctrl; + + dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); + + if (IS_ERR(imx_data->pinctrl) || + IS_ERR(imx_data->pins_default) || + IS_ERR(imx_data->pins_100mhz) || + IS_ERR(imx_data->pins_200mhz)) + return -EINVAL; + + switch (uhs) { + case MMC_TIMING_UHS_SDR50: + pinctrl = imx_data->pins_100mhz; + break; + case MMC_TIMING_UHS_SDR104: + pinctrl = imx_data->pins_200mhz; + break; + default: + /* back to default state for other legacy timing */ + pinctrl = imx_data->pins_default; + } + + return pinctrl_select_state(imx_data->pinctrl, pinctrl); +} + +static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + + switch (uhs) { + case MMC_TIMING_UHS_SDR12: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12; + break; + case MMC_TIMING_UHS_SDR25: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25; + break; + case MMC_TIMING_UHS_SDR50: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; + break; + case MMC_TIMING_UHS_SDR104: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; + break; + case MMC_TIMING_UHS_DDR50: + imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50; + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | + ESDHC_MIX_CTRL_DDREN, + host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 1; + if (boarddata->delay_line) { + u32 v; + v = boarddata->delay_line << + ESDHC_DLL_OVERRIDE_VAL_SHIFT | + (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); + if (is_imx53_esdhc(imx_data)) + v <<= 1; + writel(v, host->ioaddr + ESDHC_DLL_CTRL); + } + break; + } + + return esdhc_change_pinstate(host, uhs); +} + +static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, .write_l = esdhc_writel_le, @@ -465,6 +870,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .get_min_clock = esdhc_pltfm_get_min_clock, .get_ro = esdhc_pltfm_get_ro, .platform_bus_width = esdhc_pltfm_bus_width, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { @@ -506,6 +912,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "max-frequency", &boarddata->f_max); + if (of_find_property(np, "no-1-8-v", NULL)) + boarddata->support_vsel = false; + else + boarddata->support_vsel = true; + + if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) + boarddata->delay_line = 0; + return 0; } #else @@ -539,9 +953,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto free_sdhci; } - if (of_id) - pdev->id_entry = of_id->data; - imx_data->devtype = pdev->id_entry->driver_data; + imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *) + pdev->id_entry->driver_data; pltfm_host->priv = imx_data; imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); @@ -568,29 +981,39 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) clk_prepare_enable(imx_data->clk_ipg); clk_prepare_enable(imx_data->clk_ahb); - imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + imx_data->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(imx_data->pinctrl)) { err = PTR_ERR(imx_data->pinctrl); goto disable_clk; } + imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx_data->pins_default)) { + err = PTR_ERR(imx_data->pins_default); + dev_err(mmc_dev(host->mmc), "could not get default state\n"); + goto disable_clk; + } + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) + if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207) /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK | SDHCI_QUIRK_BROKEN_ADMA; - if (is_imx53_esdhc(imx_data)) - imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; - /* * The imx6q ROM code will change the default watermark level setting * to something insane. Change it back here. */ - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) { writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; + } + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) + sdhci_esdhc_ops.platform_execute_tuning = + esdhc_executing_tuning; boarddata = &imx_data->boarddata; if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { if (!host->mmc->parent->platform_data) { @@ -650,6 +1073,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) break; } + /* sdr50 and sdr104 needs work on 1.8v signal voltage */ + if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) { + imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, + ESDHC_PINCTRL_STATE_100MHZ); + imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, + ESDHC_PINCTRL_STATE_200MHZ); + if (IS_ERR(imx_data->pins_100mhz) || + IS_ERR(imx_data->pins_200mhz)) { + dev_warn(mmc_dev(host->mmc), + "could not get ultra high speed state, work on normal mode\n"); + /* fall back to not support uhs by specify no 1.8v quirk */ + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + } else { + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + err = sdhci_add_host(host); if (err) goto disable_clk; diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a2a06420e463..a7d9f95a7b03 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -49,41 +49,4 @@ #define ESDHC_HOST_CONTROL_RES 0x05 -static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock, - unsigned int host_clock) -{ - int pre_div = 2; - int div = 1; - u32 temp; - - if (clock == 0) - goto out; - - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN - | ESDHC_CLOCK_MASK); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - - while (host_clock / pre_div / 16 > clock && pre_div < 256) - pre_div *= 2; - - while (host_clock / pre_div / div > clock && div < 16) - div++; - - dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host_clock / pre_div / div); - - pre_div >>= 1; - div--; - - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN - | (div << ESDHC_DIVIDER_SHIFT) - | (pre_div << ESDHC_PREDIV_SHIFT)); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - mdelay(1); -out: - host->clock = clock; -} - #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index e328252ebf2a..0b249970b119 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -199,6 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { + + int pre_div = 2; + int div = 1; + u32 temp; + + if (clock == 0) + goto out; + /* Workaround to reduce the clock frequency for p1010 esdhc */ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { if (clock > 20000000) @@ -207,8 +215,31 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) clock -= 5000000; } - /* Set the clock */ - esdhc_set_clock(host, clock, host->max_clk); + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | ESDHC_CLOCK_MASK); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + while (host->max_clk / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; + + while (host->max_clk / pre_div / div > clock && div < 16) + div++; + + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->max_clk / pre_div / div); + + pre_div >>= 1; + div--; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | (div << ESDHC_DIVIDER_SHIFT) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + mdelay(1); +out: + host->clock = clock; } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index d7d6bc8968d2..8f753811fc7a 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -37,6 +37,12 @@ #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 +#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa +#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb +#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5 +#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6 /* * PCI registers @@ -356,6 +362,28 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { .allow_runtime_pm = true, }; +/* Define Host controllers for Intel Merrifield platform */ +#define INTEL_MRFL_EMMC_0 0 +#define INTEL_MRFL_EMMC_1 1 + +static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ + if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) && + (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1)) + /* SD support is not ready yet */ + return -ENODEV; + + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_1_8V_DDR; + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .probe_slot = intel_mrfl_mmc_probe_slot, +}; + /* O2Micro extra registers */ #define O2_SD_LOCK_WP 0xD3 #define O2_SD_MULTI_VCC3V 0xEE @@ -939,6 +967,54 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_EMMC0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_EMMC1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRFL_MMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc, + }, { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7a7fb4f0d5a4..bd8a0982aec3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -49,7 +49,6 @@ static unsigned int debug_quirks2; static void sdhci_finish_data(struct sdhci_host *); -static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_tuning_timer(unsigned long data); @@ -981,7 +980,7 @@ static void sdhci_finish_data(struct sdhci_host *host) tasklet_schedule(&host->finish_tasklet); } -static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) +void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; u32 mask; @@ -1053,6 +1052,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); } +EXPORT_SYMBOL_GPL(sdhci_send_command); static void sdhci_finish_command(struct sdhci_host *host) { @@ -1435,7 +1435,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } if (host->version >= SDHCI_SPEC_300 && - (ios->power_mode == MMC_POWER_UP)) + (ios->power_mode == MMC_POWER_UP) && + !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); sdhci_set_clock(host, ios->clock); @@ -1875,6 +1876,14 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) return 0; } + if (host->ops->platform_execute_tuning) { + spin_unlock(&host->lock); + enable_irq(host->irq); + err = host->ops->platform_execute_tuning(host, opcode); + sdhci_runtime_pm_put(host); + return err; + } + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); /* @@ -1981,6 +1990,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (!tuning_loop_counter || !timeout) { ctrl &= ~SDHCI_CTRL_TUNED_CLK; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + err = -EIO; } else { if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { pr_info(DRIVER_NAME ": Tuning procedure" @@ -2491,6 +2501,14 @@ again: result = IRQ_HANDLED; intmask = sdhci_readl(host, SDHCI_INT_STATUS); + + /* + * If we know we'll call the driver to signal SDIO IRQ, disregard + * further indications of Card Interrupt in the status to avoid a + * needless loop. + */ + if (cardint) + intmask &= ~SDHCI_INT_CARD_INT; if (intmask && --max_loops) goto again; out: @@ -2546,8 +2564,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { - int ret; - if (host->ops->platform_suspend) host->ops->platform_suspend(host); @@ -2559,19 +2575,6 @@ int sdhci_suspend_host(struct sdhci_host *host) host->flags &= ~SDHCI_NEEDS_RETUNING; } - ret = mmc_suspend_host(host->mmc); - if (ret) { - if (host->flags & SDHCI_USING_RETUNING_TIMER) { - host->flags |= SDHCI_NEEDS_RETUNING; - mod_timer(&host->tuning_timer, jiffies + - host->tuning_count * HZ); - } - - sdhci_enable_card_detection(host); - - return ret; - } - if (!device_may_wakeup(mmc_dev(host->mmc))) { sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); @@ -2579,14 +2582,14 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_enable_irq_wakeups(host); enable_irq_wake(host->irq); } - return ret; + return 0; } EXPORT_SYMBOL_GPL(sdhci_suspend_host); int sdhci_resume_host(struct sdhci_host *host) { - int ret; + int ret = 0; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) @@ -2615,7 +2618,6 @@ int sdhci_resume_host(struct sdhci_host *host) mmiowb(); } - ret = mmc_resume_host(host->mmc); sdhci_enable_card_detection(host); if (host->ops->platform_resume) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b037f188fe44..0a3ed01887db 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -288,6 +288,7 @@ struct sdhci_ops { unsigned int (*get_ro)(struct sdhci_host *host); void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); + int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); void (*platform_suspend)(struct sdhci_host *host); @@ -393,6 +394,8 @@ static inline void *sdhci_priv(struct sdhci_host *host) extern void sdhci_card_detect(struct sdhci_host *host); extern int sdhci_add_host(struct sdhci_host *host); extern void sdhci_remove_host(struct sdhci_host *host, int dead); +extern void sdhci_send_command(struct sdhci_host *host, + struct mmc_command *cmd); #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 50adbd155f35..b7e305775314 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -516,9 +516,7 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link) #ifdef CONFIG_PM static int sdricoh_pcmcia_suspend(struct pcmcia_device *link) { - struct mmc_host *mmc = link->priv; dev_dbg(&link->dev, "suspend\n"); - mmc_suspend_host(mmc); return 0; } @@ -527,7 +525,6 @@ static int sdricoh_pcmcia_resume(struct pcmcia_device *link) struct mmc_host *mmc = link->priv; dev_dbg(&link->dev, "resume\n"); sdricoh_reset(mmc_priv(mmc)); - mmc_resume_host(mmc); return 0; } #else diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 36629a024aa1..d032b080ac4d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -964,7 +964,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) static int sh_mmcif_clk_update(struct sh_mmcif_host *host) { - int ret = clk_enable(host->hclk); + int ret = clk_prepare_enable(host->hclk); if (!ret) { host->clk = clk_get_rate(host->hclk); @@ -1018,7 +1018,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power) { pm_runtime_put_sync(&host->pd->dev); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); host->power = false; if (ios->power_mode == MMC_POWER_OFF) sh_mmcif_set_power(host, ios); @@ -1466,7 +1466,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) mutex_init(&host->thread_lock); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) goto emmcaddh; @@ -1487,7 +1487,7 @@ ereqirq1: ereqirq0: pm_runtime_suspend(&pdev->dev); eresume: - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); eclkupdate: clk_put(host->hclk); eclkget: @@ -1505,7 +1505,7 @@ static int sh_mmcif_remove(struct platform_device *pdev) int irq[2]; host->dying = true; - clk_enable(host->hclk); + clk_prepare_enable(host->hclk); pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); @@ -1530,7 +1530,7 @@ static int sh_mmcif_remove(struct platform_device *pdev) if (irq[1] >= 0) free_irq(irq[1], host); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1538,28 +1538,21 @@ static int sh_mmcif_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sh_mmcif_suspend(struct device *dev) { struct sh_mmcif_host *host = dev_get_drvdata(dev); - int ret = mmc_suspend_host(host->mmc); - if (!ret) - sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - return ret; + return 0; } static int sh_mmcif_resume(struct device *dev) { - struct sh_mmcif_host *host = dev_get_drvdata(dev); - - return mmc_resume_host(host->mmc); + return 0; } -#else -#define sh_mmcif_suspend NULL -#define sh_mmcif_resume NULL -#endif /* CONFIG_PM */ +#endif static const struct of_device_id mmcif_of_match[] = { { .compatible = "renesas,sh-mmcif" }, @@ -1568,8 +1561,7 @@ static const struct of_device_id mmcif_of_match[] = { MODULE_DEVICE_TABLE(of, mmcif_of_match); static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { - .suspend = sh_mmcif_suspend, - .resume = sh_mmcif_resume, + SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume) }; static struct platform_driver sh_mmcif_driver = { diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 43d962829f8e..d1760ebcac03 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -1030,7 +1030,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) { - return mmc_suspend_host(tifm_get_drvdata(sock)); + return 0; } static int tifm_sd_resume(struct tifm_dev *sock) @@ -1044,8 +1044,6 @@ static int tifm_sd_resume(struct tifm_dev *sock) if (rc) host->eject = 1; - else - rc = mmc_resume_host(mmc); return rc; } diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b3802256f954..f3b2d8ca1eca 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1145,12 +1145,9 @@ int tmio_mmc_host_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - int ret = mmc_suspend_host(mmc); - if (!ret) - tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); - - return ret; + tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); + return 0; } EXPORT_SYMBOL(tmio_mmc_host_suspend); @@ -1163,7 +1160,7 @@ int tmio_mmc_host_resume(struct device *dev) /* The MMC core will perform the complete set up */ host->resuming = true; - return mmc_resume_host(mmc); + return 0; } EXPORT_SYMBOL(tmio_mmc_host_resume); diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 4f84586c6e9e..63fac78b3d46 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1269,21 +1269,18 @@ static void via_init_sdc_pm(struct via_crdr_mmc_host *host) static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state) { struct via_crdr_mmc_host *host; - int ret = 0; host = pci_get_drvdata(pcidev); via_save_pcictrlreg(host); via_save_sdcreg(host); - ret = mmc_suspend_host(host->mmc); - pci_save_state(pcidev); pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); pci_disable_device(pcidev); pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); - return ret; + return 0; } static int via_sd_resume(struct pci_dev *pcidev) @@ -1316,8 +1313,6 @@ static int via_sd_resume(struct pci_dev *pcidev) via_restore_pcictrlreg(sdhost); via_init_sdc_pm(sdhost); - ret = mmc_resume_host(sdhost->mmc); - return ret; } diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index e9028ad05ffb..4262296c12fa 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2392,26 +2392,12 @@ static void vub300_disconnect(struct usb_interface *interface) #ifdef CONFIG_PM static int vub300_suspend(struct usb_interface *intf, pm_message_t message) { - struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); - if (!vub300 || !vub300->mmc) { - return 0; - } else { - struct mmc_host *mmc = vub300->mmc; - mmc_suspend_host(mmc); - return 0; - } + return 0; } static int vub300_resume(struct usb_interface *intf) { - struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); - if (!vub300 || !vub300->mmc) { - return 0; - } else { - struct mmc_host *mmc = vub300->mmc; - mmc_resume_host(mmc); - return 0; - } + return 0; } #else #define vub300_suspend NULL diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index e954b7758876..1defd5ed3236 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1814,28 +1814,11 @@ static void wbsd_pnp_remove(struct pnp_dev *dev) #ifdef CONFIG_PM -static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) -{ - BUG_ON(host == NULL); - - return mmc_suspend_host(host->mmc); -} - -static int wbsd_resume(struct wbsd_host *host) -{ - BUG_ON(host == NULL); - - wbsd_init_device(host); - - return mmc_resume_host(host->mmc); -} - static int wbsd_platform_suspend(struct platform_device *dev, pm_message_t state) { struct mmc_host *mmc = platform_get_drvdata(dev); struct wbsd_host *host; - int ret; if (mmc == NULL) return 0; @@ -1844,12 +1827,7 @@ static int wbsd_platform_suspend(struct platform_device *dev, host = mmc_priv(mmc); - ret = wbsd_suspend(host, state); - if (ret) - return ret; - wbsd_chip_poweroff(host); - return 0; } @@ -1872,7 +1850,8 @@ static int wbsd_platform_resume(struct platform_device *dev) */ mdelay(5); - return wbsd_resume(host); + wbsd_init_device(host); + return 0; } #ifdef CONFIG_PNP @@ -1880,16 +1859,12 @@ static int wbsd_platform_resume(struct platform_device *dev) static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) { struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); - struct wbsd_host *host; if (mmc == NULL) return 0; DBGF("Suspending...\n"); - - host = mmc_priv(mmc); - - return wbsd_suspend(host, state); + return 0; } static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) @@ -1922,7 +1897,8 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) */ mdelay(5); - return wbsd_resume(host); + wbsd_init_device(host); + return 0; } #endif /* CONFIG_PNP */ diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 34231d5168fc..e902ed7846b0 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -212,28 +212,14 @@ struct wmt_mci_priv { static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) { - u32 reg_tmp; - if (enable) { - if (priv->power_inverted) { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SD_OFF, - priv->sdmmc_base + SDMMC_BUSMODE); - } else { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp & (~BM_SD_OFF), - priv->sdmmc_base + SDMMC_BUSMODE); - } - } else { - if (priv->power_inverted) { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp & (~BM_SD_OFF), - priv->sdmmc_base + SDMMC_BUSMODE); - } else { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SD_OFF, - priv->sdmmc_base + SDMMC_BUSMODE); - } - } + u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + + if (enable ^ priv->power_inverted) + reg_tmp &= ~BM_SD_OFF; + else + reg_tmp |= BM_SD_OFF; + + writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE); } static void wmt_mci_read_response(struct mmc_host *mmc) @@ -939,28 +925,23 @@ static int wmt_mci_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct mmc_host *mmc = platform_get_drvdata(pdev); struct wmt_mci_priv *priv; - int ret; if (!mmc) return 0; priv = mmc_priv(mmc); - ret = mmc_suspend_host(mmc); - - if (!ret) { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + - SDMMC_BUSMODE); + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); - reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); - writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); - writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); - writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); - clk_disable(priv->clk_sdmmc); - } - return ret; + clk_disable(priv->clk_sdmmc); + return 0; } static int wmt_mci_resume(struct device *dev) @@ -969,7 +950,6 @@ static int wmt_mci_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct mmc_host *mmc = platform_get_drvdata(pdev); struct wmt_mci_priv *priv; - int ret = 0; if (mmc) { priv = mmc_priv(mmc); @@ -987,10 +967,9 @@ static int wmt_mci_resume(struct device *dev) writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + SDMMC_INTMASK0); - ret = mmc_resume_host(mmc); } - return ret; + return 0; } static const struct dev_pm_ops wmt_mci_pm = { |