diff options
author | Scott Wood <scottwood@freescale.com> | 2014-04-08 01:00:49 (GMT) |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2014-04-08 19:58:35 (GMT) |
commit | 47d2261a3fa71cde24263559a4219a25e50d8c89 (patch) | |
tree | 28774d5b330ccf1b777a3af222d8356918328013 /drivers/mmc | |
parent | fb7f27080adc65cd5f341bdf56a1d0c14f316c1b (diff) | |
parent | 5fb9d37f27351e42f002e372074249f92cbdf815 (diff) | |
download | linux-fsl-qoriq-47d2261a3fa71cde24263559a4219a25e50d8c89.tar.xz |
Merge branch 'merge' into sdk-v1.6.x
This reverts v3.13-rc3+ (78fd82238d0e5716) to v3.12, except for
commits which I noticed which appear relevant to the SDK.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Conflicts:
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/book3s_interrupts.S
arch/powerpc/kvm/e500.c
arch/powerpc/kvm/e500mc.c
arch/powerpc/sysdev/fsl_soc.h
drivers/Kconfig
drivers/cpufreq/ppc-corenet-cpufreq.c
drivers/dma/fsldma.c
drivers/dma/s3c24xx-dma.c
drivers/misc/Makefile
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mtd/devices/m25p80.c
drivers/net/ethernet/freescale/gianfar.h
drivers/platform/Kconfig
drivers/platform/Makefile
drivers/spi/spi-fsl-espi.c
include/crypto/algapi.h
include/linux/netdev_features.h
include/linux/skbuff.h
include/net/ip.h
net/core/ethtool.c
Diffstat (limited to 'drivers/mmc')
49 files changed, 1314 insertions, 1913 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8eb28c0..f70f3e4 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2521,6 +2521,7 @@ 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); @@ -2555,6 +2556,7 @@ static int mmc_blk_resume(struct mmc_card *card) list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); } + pm_runtime_put(&card->dev); } return 0; } diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 50fb07e..b0f6bfd 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -15,7 +15,6 @@ #include <linux/freezer.h> #include <linux/kthread.h> #include <linux/scatterlist.h> -#include <linux/dma-mapping.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -201,7 +200,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, struct mmc_queue_req *mqrq_prev = &mq->mqrq[1]; if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) - limit = dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT; + limit = *mmc_dev(host)->dma_mask; mq->card = card; mq->queue = blk_init_queue(mmc_request_fn, lock); diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 64145a3..704bf66 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -27,7 +27,7 @@ #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) -static ssize_t type_show(struct device *dev, +static ssize_t mmc_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mmc_card *card = mmc_dev_to_card(dev); @@ -45,13 +45,11 @@ static ssize_t type_show(struct device *dev, return -EFAULT; } } -static DEVICE_ATTR_RO(type); -static struct attribute *mmc_dev_attrs[] = { - &dev_attr_type.attr, - NULL, +static struct device_attribute mmc_dev_attrs[] = { + __ATTR(type, S_IRUGO, mmc_type_show, NULL), + __ATTR_NULL, }; -ATTRIBUTE_GROUPS(mmc_dev); /* * This currently matches any MMC driver to any MMC card - drivers @@ -220,7 +218,7 @@ static const struct dev_pm_ops mmc_bus_pm_ops = { static struct bus_type mmc_bus_type = { .name = "mmc", - .dev_groups = mmc_dev_groups, + .dev_attrs = mmc_dev_attrs, .match = mmc_bus_match, .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, @@ -342,7 +340,7 @@ int mmc_add_card(struct mmc_card *card) break; } - if (mmc_card_uhs(card) && + if (mmc_sd_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 4ecaac7..59cf22a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,7 +23,6 @@ #include <linux/log2.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> -#include <linux/pm_wakeup.h> #include <linux/suspend.h> #include <linux/fault-inject.h> #include <linux/random.h> @@ -302,7 +301,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) } err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true); + EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal); if (err) { pr_warn("%s: Error %d starting bkops\n", mmc_hostname(card->host), err); @@ -931,6 +930,31 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) EXPORT_SYMBOL(__mmc_claim_host); /** + * mmc_try_claim_host - try exclusively to claim a host + * @host: mmc host to claim + * + * Returns %1 if the host is claimed, %0 otherwise. + */ +int mmc_try_claim_host(struct mmc_host *host) +{ + int claimed_host = 0; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (!host->claimed || host->claimer == current) { + host->claimed = 1; + host->claimer = current; + host->claim_cnt += 1; + claimed_host = 1; + } + spin_unlock_irqrestore(&host->lock, flags); + if (host->ops->enable && claimed_host && host->claim_cnt == 1) + host->ops->enable(host); + return claimed_host; +} +EXPORT_SYMBOL(mmc_try_claim_host); + +/** * mmc_release_host - release a host * @host: mmc host to release * @@ -1370,31 +1394,22 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) { int bit; - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - dev_warn(mmc_dev(host), - "card claims to support voltages below defined range\n"); - ocr &= ~0x7F; - } - ocr &= host->ocr_avail; - if (!ocr) { - dev_warn(mmc_dev(host), "no support for card's volts\n"); - return 0; - } - if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) { - bit = ffs(ocr) - 1; + bit = ffs(ocr); + if (bit) { + bit -= 1; + ocr &= 3 << bit; - mmc_power_cycle(host, ocr); + + mmc_host_clk_hold(host); + host->ios.vdd = bit; + mmc_set_ios(host); + mmc_host_clk_release(host); } else { - bit = fls(ocr) - 1; - ocr &= 3 << bit; - if (bit != host->ios.vdd) - dev_warn(mmc_dev(host), "exceeding card's volts\n"); + pr_warning("%s: host doesn't support card's voltages\n", + mmc_hostname(host)); + ocr = 0; } return ocr; @@ -1419,7 +1434,7 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) } -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) { struct mmc_command cmd = {0}; int err = 0; @@ -1501,7 +1516,7 @@ power_cycle: if (err) { pr_debug("%s: Signal voltage switch failed, " "power cycling card\n", mmc_hostname(host)); - mmc_power_cycle(host, ocr); + mmc_power_cycle(host); } mmc_host_clk_release(host); @@ -1542,14 +1557,22 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ -void mmc_power_up(struct mmc_host *host, u32 ocr) +void mmc_power_up(struct mmc_host *host) { + int bit; + if (host->ios.power_mode == MMC_POWER_ON) return; mmc_host_clk_hold(host); - host->ios.vdd = fls(ocr) - 1; + /* If ocr is set, we use it */ + if (host->ocr) + bit = ffs(host->ocr) - 1; + else + bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; if (mmc_host_is_spi(host)) host->ios.chip_select = MMC_CS_HIGH; else @@ -1593,6 +1616,13 @@ void mmc_power_off(struct mmc_host *host) host->ios.clock = 0; host->ios.vdd = 0; + + /* + * Reset ocr mask to be the highest possible voltage supported for + * this mmc host. This value will be used at next power up. + */ + host->ocr = 1 << (fls(host->ocr_avail) - 1); + if (!mmc_host_is_spi(host)) { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; @@ -1612,12 +1642,12 @@ void mmc_power_off(struct mmc_host *host) mmc_host_clk_release(host); } -void mmc_power_cycle(struct mmc_host *host, u32 ocr) +void mmc_power_cycle(struct mmc_host *host) { mmc_power_off(host); /* Wait at least 1 ms according to SD spec */ mmc_delay(1); - mmc_power_up(host, ocr); + mmc_power_up(host); } /* @@ -1705,28 +1735,6 @@ void mmc_detach_bus(struct mmc_host *host) mmc_bus_put(host); } -static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, - bool cd_irq) -{ -#ifdef CONFIG_MMC_DEBUG - unsigned long flags; - spin_lock_irqsave(&host->lock, flags); - WARN_ON(host->removed); - spin_unlock_irqrestore(&host->lock, flags); -#endif - - /* - * If the device is configured as wakeup, we prevent a new sleep for - * 5 s to give provision for user space to consume the event. - */ - if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) && - device_can_wakeup(mmc_dev(host))) - pm_wakeup_event(mmc_dev(host), 5000); - - host->detect_change = 1; - mmc_schedule_delayed_work(&host->detect, delay); -} - /** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. @@ -1739,8 +1747,16 @@ static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { - _mmc_detect_change(host, delay, true); +#ifdef CONFIG_MMC_DEBUG + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); + WARN_ON(host->removed); + spin_unlock_irqrestore(&host->lock, flags); +#endif + host->detect_change = 1; + mmc_schedule_delayed_work(&host->detect, delay); } + EXPORT_SYMBOL(mmc_detect_change); void mmc_init_erase(struct mmc_card *card) @@ -2335,7 +2351,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) pr_info("%s: %s: trying to init card at %u Hz\n", mmc_hostname(host), __func__, host->f_init); #endif - mmc_power_up(host, host->ocr_avail); + mmc_power_up(host); /* * Some eMMCs (with VCCQ always on) may not be reset after power up, so @@ -2429,7 +2445,7 @@ int mmc_detect_card_removed(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); cancel_delayed_work(&host->detect); - _mmc_detect_change(host, 0, false); + mmc_detect_change(host, 0); } } @@ -2510,8 +2526,8 @@ void mmc_start_host(struct mmc_host *host) if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else - mmc_power_up(host, host->ocr_avail); - _mmc_detect_change(host, 0, false); + mmc_power_up(host); + mmc_detect_change(host, 0); } void mmc_stop_host(struct mmc_host *host) @@ -2589,7 +2605,7 @@ int mmc_power_restore_host(struct mmc_host *host) return -EINVAL; } - mmc_power_up(host, host->card->ocr); + mmc_power_up(host); ret = host->bus_ops->power_restore(host); mmc_bus_put(host); @@ -2663,6 +2679,28 @@ EXPORT_SYMBOL(mmc_cache_ctrl); #ifdef CONFIG_PM +/** + * mmc_suspend_host - suspend a host + * @host: mmc host + */ +int mmc_suspend_host(struct mmc_host *host) +{ + /* This function is deprecated */ + return 0; +} +EXPORT_SYMBOL(mmc_suspend_host); + +/** + * mmc_resume_host - resume a previously suspended host + * @host: mmc host + */ +int mmc_resume_host(struct mmc_host *host) +{ + /* This function is deprecated */ + return 0; +} +EXPORT_SYMBOL(mmc_resume_host); + /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able to sync the card. @@ -2708,7 +2746,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; spin_unlock_irqrestore(&host->lock, flags); - _mmc_detect_change(host, 0, false); + mmc_detect_change(host, 0); } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 443a584..5345d15 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, 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); 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, u32 ocr); +void mmc_power_up(struct mmc_host *host); void mmc_power_off(struct mmc_host *host); -void mmc_power_cycle(struct mmc_host *host, u32 ocr); +void mmc_power_cycle(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f631f5a..6d02012 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -13,7 +13,6 @@ #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> @@ -935,7 +934,6 @@ 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)); @@ -1406,9 +1404,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, true, false); + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + notify_type, timeout); if (err) pr_err("%s: Power Off Notification timed out, %u\n", mmc_hostname(card->host), timeout); @@ -1479,9 +1477,6 @@ 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) @@ -1501,93 +1496,51 @@ 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 + * Suspend callback from host. */ static int mmc_suspend(struct mmc_host *host) { - int err; - - err = _mmc_suspend(host, true); - if (!err) { - pm_runtime_disable(&host->card->dev); - pm_runtime_set_suspended(&host->card->dev); - } + return _mmc_suspend(host, true); +} - return err; +/* + * Shutdown callback + */ +static int mmc_shutdown(struct mmc_host *host) +{ + return _mmc_suspend(host, false); } /* + * Resume callback from host. + * * This function tries to determine if the same card is still present * 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 = 0; + int err; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); - - 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_power_up(host); + mmc_select_voltage(host, host->ocr); + err = mmc_init_card(host, host->ocr, host->card); 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. @@ -1599,11 +1552,18 @@ static int mmc_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = _mmc_suspend(host, true); - if (err) + mmc_claim_host(host); + + err = mmc_suspend(host); + if (err) { pr_err("%s: error %d doing aggessive suspend\n", mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); +out: + mmc_release_host(host); return err; } @@ -1614,14 +1574,18 @@ static int mmc_runtime_resume(struct mmc_host *host) { int err; - if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = _mmc_resume(host); + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", mmc_hostname(host), err); + mmc_release_host(host); return 0; } @@ -1631,7 +1595,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->card->ocr, host->card); + ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return ret; @@ -1676,7 +1640,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host) int mmc_attach_mmc(struct mmc_host *host) { int err; - u32 ocr, rocr; + u32 ocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1702,12 +1666,23 @@ int mmc_attach_mmc(struct mmc_host *host) goto err; } - rocr = mmc_select_voltage(host, ocr); + /* + * 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); /* * Can we support the voltage of the card? */ - if (!rocr) { + if (!host->ocr) { err = -EINVAL; goto err; } @@ -1715,7 +1690,7 @@ int mmc_attach_mmc(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_init_card(host, rocr, NULL); + err = mmc_init_card(host, host->ocr, NULL); if (err) goto err; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index e5b5eeb..ef18348 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,40 +23,6 @@ #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; @@ -404,18 +370,16 @@ 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, bool send_status) + unsigned int timeout_ms, bool use_busy_signal) { int err; struct mmc_command cmd = {0}; unsigned long timeout; - u32 status = 0; - bool ignore_crc = false; + u32 status; BUG_ON(!card); BUG_ON(!card->host); @@ -444,37 +408,17 @@ 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 - * 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; - + /* Must check status to be sure of no errors */ timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); do { - if (send_status) { - err = __mmc_send_status(card, &status, ignore_crc); - if (err) - return err; - } + err = mmc_send_status(card, &status); + 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", @@ -501,10 +445,36 @@ 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, true); + return __mmc_switch(card, set, index, value, timeout_ms, 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 6f42050..5e8823d 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -13,7 +13,6 @@ #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> @@ -722,7 +721,6 @@ 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) { @@ -775,8 +773,7 @@ try_again: */ if (!mmc_host_is_spi(host) && rocr && ((*rocr & 0x41000000) == 0x41000000)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - pocr); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); if (err == -EAGAIN) { retries--; goto try_again; @@ -938,7 +935,6 @@ 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)); } @@ -1068,7 +1064,10 @@ static void mmc_sd_detect(struct mmc_host *host) } } -static int _mmc_sd_suspend(struct mmc_host *host) +/* + * Suspend callback from host. + */ +static int mmc_sd_suspend(struct mmc_host *host) { int err = 0; @@ -1076,77 +1075,34 @@ 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 = 0; + int err; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); - - 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_power_up(host); + mmc_select_voltage(host, host->ocr); + err = mmc_sd_init_card(host, host->ocr, host->card); mmc_release_host(host); - return err; -} - -/* - * Callback for 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; } @@ -1161,11 +1117,18 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = _mmc_sd_suspend(host); - if (err) + mmc_claim_host(host); + + err = mmc_sd_suspend(host); + if (err) { pr_err("%s: error %d doing aggessive suspend\n", mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); +out: + mmc_release_host(host); return err; } @@ -1176,14 +1139,18 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) { int err; - if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = _mmc_sd_resume(host); + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_sd_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", mmc_hostname(host), err); + mmc_release_host(host); return 0; } @@ -1193,7 +1160,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->card->ocr, host->card); + ret = mmc_sd_init_card(host, host->ocr, host->card); mmc_release_host(host); return ret; @@ -1238,7 +1205,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) int mmc_attach_sd(struct mmc_host *host) { int err; - u32 ocr, rocr; + u32 ocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1262,12 +1229,31 @@ int mmc_attach_sd(struct mmc_host *host) goto err; } - rocr = mmc_select_voltage(host, ocr); + /* + * 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); /* * Can we support the voltage(s) of the card(s)? */ - if (!rocr) { + if (!host->ocr) { err = -EINVAL; goto err; } @@ -1275,7 +1261,7 @@ int mmc_attach_sd(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_sd_init_card(host, rocr, NULL); + err = mmc_sd_init_card(host, host->ocr, NULL); if (err) goto err; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 4d721c6..80d89cff 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -593,28 +593,23 @@ 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, ocr, &rocr); + err = mmc_send_io_op_cond(host, host->ocr, &ocr); if (err) goto err; } @@ -637,8 +632,8 @@ try_again: goto err; } - if ((rocr & R4_MEMORY_PRESENT) && - mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) { + if ((ocr & R4_MEMORY_PRESENT) && + mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) { card->type = MMC_TYPE_SD_COMBO; if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || @@ -668,9 +663,8 @@ try_again: * systems that claim 1.8v signalling in fact do not support * it. */ - if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - ocr); + if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); if (err == -EAGAIN) { sdio_reset(host); mmc_go_idle(host); @@ -680,10 +674,12 @@ 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; } /* @@ -763,7 +759,6 @@ try_again: card = oldcard; } - card->ocr = ocr_card; mmc_fixup_device(card, NULL); if (card->type == MMC_TYPE_SD_COMBO) { @@ -986,7 +981,8 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Restore power if needed */ if (!mmc_card_keep_power(host)) { - mmc_power_up(host, host->card->ocr); + mmc_power_up(host); + mmc_select_voltage(host, host->ocr); /* * Tell runtime PM core we just powered up the card, * since it still believes the card is powered off. @@ -1004,7 +1000,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->card->ocr, host->card, + err = mmc_sdio_init_card(host, host->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 */ @@ -1044,6 +1040,7 @@ 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); @@ -1065,17 +1062,32 @@ 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, NULL); + ret = mmc_send_io_op_cond(host, 0, &ocr); if (ret) goto out; - ret = mmc_sdio_init_card(host, host->card->ocr, host->card, + 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, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); @@ -1096,7 +1108,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, host->card->ocr); + mmc_power_up(host); return mmc_sdio_power_restore(host); } @@ -1119,7 +1131,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { int mmc_attach_sdio(struct mmc_host *host) { int err, i, funcs; - u32 ocr, rocr; + u32 ocr; struct mmc_card *card; BUG_ON(!host); @@ -1133,13 +1145,23 @@ 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; + } - rocr = mmc_select_voltage(host, ocr); + host->ocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage(s) of the card(s)? */ - if (!rocr) { + if (!host->ocr) { err = -EINVAL; goto err; } @@ -1147,10 +1169,22 @@ int mmc_attach_sdio(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_sdio_init_card(host, rocr, NULL, 0); - if (err) - goto err; + 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, 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/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 157b570..6d67492 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -34,8 +34,7 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ \ func = dev_to_sdio_func (dev); \ return sprintf (buf, format_string, func->field); \ -} \ -static DEVICE_ATTR_RO(field) +} sdio_config_attr(class, "0x%02x\n"); sdio_config_attr(vendor, "0x%04x\n"); @@ -48,16 +47,14 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n", func->class, func->vendor, func->device); } -static DEVICE_ATTR_RO(modalias); - -static struct attribute *sdio_dev_attrs[] = { - &dev_attr_class.attr, - &dev_attr_vendor.attr, - &dev_attr_device.attr, - &dev_attr_modalias.attr, - NULL, + +static struct device_attribute sdio_dev_attrs[] = { + __ATTR_RO(class), + __ATTR_RO(vendor), + __ATTR_RO(device), + __ATTR_RO(modalias), + __ATTR_NULL, }; -ATTRIBUTE_GROUPS(sdio_dev); static const struct sdio_device_id *sdio_match_one(struct sdio_func *func, const struct sdio_device_id *id) @@ -228,7 +225,7 @@ static const struct dev_pm_ops sdio_bus_pm_ops = { static struct bus_type sdio_bus_type = { .name = "sdio", - .dev_groups = sdio_dev_groups, + .dev_attrs = sdio_dev_attrs, .match = sdio_bus_match, .uevent = sdio_bus_uevent, .probe = sdio_bus_probe, @@ -308,7 +305,8 @@ static void sdio_acpi_set_handle(struct sdio_func *func) struct mmc_host *host = func->card->host; u64 addr = (host->slotno << 16) | func->num; - acpi_preset_companion(&func->dev, ACPI_HANDLE(host->parent), addr); + ACPI_HANDLE_SET(&func->dev, + acpi_get_child(ACPI_HANDLE(host->parent), addr)); } #else static inline void sdio_acpi_set_handle(struct sdio_func *func) {} diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 2cbb451..69e438e 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -255,6 +255,7 @@ 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; @@ -588,13 +589,6 @@ 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; @@ -1809,14 +1803,12 @@ static void atmci_tasklet_func(unsigned long priv) if (unlikely(status)) { host->stop_transfer(host); host->data = NULL; - if (data) { - if (status & ATMCI_DTOE) { - data->error = -ETIMEDOUT; - } else if (status & ATMCI_DCRCE) { - data->error = -EILSEQ; - } else { - data->error = -EIO; - } + if (status & ATMCI_DTOE) { + data->error = -ETIMEDOUT; + } else if (status & ATMCI_DCRCE) { + data->error = -EILSEQ; + } else { + data->error = -EIO; } } @@ -2528,10 +2520,70 @@ 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 f5443a6..df9becd 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1157,6 +1157,11 @@ 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)); @@ -1173,7 +1178,7 @@ static int au1xmmc_resume(struct platform_device *pdev) au1xmmc_reset_controller(host); - return 0; + return mmc_resume_host(host->mmc); } #else #define au1xmmc_suspend NULL diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 2b7f37e..94fae2f 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -391,7 +391,6 @@ 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 @@ -416,6 +415,7 @@ 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,6 +433,7 @@ 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); @@ -639,15 +640,21 @@ 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 0; + return ret; } 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; @@ -658,6 +665,10 @@ 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 1087b4c..9d6e2b8 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -667,6 +667,12 @@ 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; @@ -675,9 +681,11 @@ 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 0; + + return mmc_resume_host(mmc); } #endif /* CONFIG_PM */ diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index d615374..e9fa87d 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -193,6 +193,7 @@ 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 @@ -1434,23 +1435,38 @@ 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; - writel(0, host->base + DAVINCI_MMCIM); - mmc_davinci_reset_ctrl(host, 1); - clk_disable(host->clk); + 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; + } - return 0; + return ret; } 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 0; + return ret; } 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 3423c5e..6a1fa21 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -14,10 +14,8 @@ #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" @@ -32,39 +30,16 @@ #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 */ @@ -73,7 +48,6 @@ struct dw_mci_exynos_priv_data { u8 ciu_div; u32 sdr_timing; u32 ddr_timing; - u32 cur_speed; }; static struct dw_mci_exynos_compatible { @@ -92,80 +66,44 @@ 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 = host->priv; + struct dw_mci_exynos_priv_data *priv; + int idx; - 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); + 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(host->dev->of_node, + exynos_compat[idx].compatible)) + priv->ctrl_type = exynos_compat[idx].ctrl_type; } + 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); - 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); + 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; 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) { @@ -183,68 +121,23 @@ 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); - /* Should be double rate for DDR mode */ - if (ios->bus_width == MMC_BUS_WIDTH_8) - wanted <<= 1; - } else { + 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; + struct dw_mci_exynos_priv_data *priv = host->priv; struct device_node *np = host->dev->of_node; u32 timing[2]; u32 div = 0; - int idx; 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; - } - - 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; - } + 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); @@ -259,131 +152,9 @@ 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 | @@ -400,7 +171,6 @@ 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[] = { @@ -410,8 +180,6 @@ 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); @@ -426,20 +194,13 @@ 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_exynos_pmops, + .pm = &dw_mci_pltfm_pmops, }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 5c49656..2089752 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -39,6 +39,7 @@ 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) @@ -58,6 +59,12 @@ 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 3e8e53a..14b5961 100644 --- a/drivers/mmc/host/dw_mmc-socfpga.c +++ b/drivers/mmc/host/dw_mmc-socfpga.c @@ -38,6 +38,21 @@ 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; } @@ -64,24 +79,12 @@ 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; + struct dw_mci_socfpga_priv_data *priv = host->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"); @@ -93,7 +96,6 @@ 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; } @@ -111,7 +113,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = { }; MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match); -static int dw_mci_socfpga_probe(struct platform_device *pdev) +int dw_mci_socfpga_probe(struct platform_device *pdev) { const struct dw_mci_drv_data *drv_data; const struct of_device_id *match; @@ -126,7 +128,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = { .remove = __exit_p(dw_mci_pltfm_remove), .driver = { .name = "dwmmc_socfpga", - .of_match_table = dw_mci_socfpga_match, + .of_match_table = of_match_ptr(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 4bce0de..018f365 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,7 +29,6 @@ #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> @@ -51,9 +50,6 @@ #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 | \ @@ -80,38 +76,41 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ -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, -}; +/** + * 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; -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, -}; + int quirks; + int wp_gpio; + + u32 ctype; + + struct mmc_request *mrq; + struct list_head queue_node; -static inline bool dw_mci_fifo_reset(struct dw_mci *host); -static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host); + 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; +}; #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -250,15 +249,10 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr = cmd->opcode; - 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)) + if (cmdr == MMC_STOP_TRANSMISSION) cmdr |= SDMMC_CMD_STOP; else - if (cmd->opcode != MMC_SEND_STATUS && cmd->data) - cmdr |= SDMMC_CMD_PRV_DAT_WAIT; + cmdr |= SDMMC_CMD_PRV_DAT_WAIT; if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ @@ -285,40 +279,6 @@ 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) { @@ -333,10 +293,9 @@ static void dw_mci_start_command(struct dw_mci *host, mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); } -static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) +static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) { - struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; - dw_mci_start_command(host, stop, host->stop_cmdr); + dw_mci_start_command(host, data->stop, host->stop_cmdr); } /* DMA interface functions */ @@ -345,10 +304,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) @@ -372,14 +331,6 @@ 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; @@ -393,7 +344,6 @@ 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); } @@ -485,7 +435,7 @@ static int dw_mci_idmac_init(struct dw_mci *host) p->des3 = host->sg_dma; p->des0 = IDMAC_DES0_ER; - dw_mci_idmac_reset(host); + mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); /* Mask out interrupts - get Tx & Rx complete only */ mci_writel(host, IDSTS, IDMAC_INT_CLR); @@ -582,78 +532,6 @@ 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; @@ -678,14 +556,6 @@ 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; @@ -711,12 +581,10 @@ 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; - dw_mci_ctrl_rd_thld(host, data); - } else { + else host->dir_status = DW_MCI_SEND_STATUS; - } if (dw_mci_submit_data_dma(host, data)) { int flags = SG_MITER_ATOMIC; @@ -738,21 +606,6 @@ 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; } } @@ -779,31 +632,24 @@ 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 (!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) + 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) /* * move the + 1 after the divide to prevent * over-clocking the card. */ div += 1; - div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; + div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0; - 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); + 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); /* disable clock */ mci_writel(host, CLKENA, 0); @@ -830,12 +676,9 @@ 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); - /* keep the clock with reflecting clock dividor */ - slot->__clk_old = clock << div; + host->current_speed = slot->clock; } - host->current_speed = clock; - /* Set the current slot bus width */ mci_writel(host, CTYPE, (slot->ctype << slot->id)); } @@ -857,9 +700,7 @@ 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) { @@ -883,8 +724,6 @@ 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, @@ -967,13 +806,14 @@ 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; - /* - * Use mirror of ios->clock to prevent race with mmc - * core ios update when finding the minimum. - */ - slot->clock = ios->clock; + if (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); @@ -1099,38 +939,6 @@ 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, @@ -1139,7 +947,6 @@ 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) @@ -1171,7 +978,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) spin_lock(&host->lock); } -static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) +static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) { u32 status = host->cmd_status; @@ -1205,52 +1012,12 @@ static int 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); - } - - 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; + if (cmd->data) { + dw_mci_stop_dma(host); + host->data = NULL; } - - 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) @@ -1258,16 +1025,14 @@ 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; - unsigned int err; + u32 status, ctrl; spin_lock(&host->lock); state = host->state; data = host->data; - mrq = host->mrq; do { prev_state = state; @@ -1284,23 +1049,16 @@ static void dw_mci_tasklet_func(unsigned long priv) cmd = host->cmd; host->cmd = NULL; set_bit(EVENT_CMD_COMPLETE, &host->completed_events); - err = dw_mci_command_complete(host, cmd); - if (cmd == mrq->sbc && !err) { + dw_mci_command_complete(host, cmd); + if (cmd == host->mrq->sbc && !cmd->error) { prev_state = state = STATE_SENDING_CMD; __dw_mci_start_request(host, host->cur_slot, - mrq->cmd); + host->mrq->cmd); goto unlock; } - 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); + if (!host->mrq->data || cmd->error) { + dw_mci_request_end(host, host->mrq); goto unlock; } @@ -1311,7 +1069,8 @@ 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); - send_stop_abort(host, data); + if (data->stop) + send_stop_cmd(host, data); state = STATE_DATA_ERROR; break; } @@ -1331,27 +1090,60 @@ static void dw_mci_tasklet_func(unsigned long priv) host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); - err = dw_mci_data_complete(host, data); - - if (!err) { - if (!data->stop || mrq->sbc) { - if (mrq->sbc) - data->stop->error = 0; - dw_mci_request_end(host, mrq); - goto unlock; + 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; + } - /* stop command for open-ended transfer*/ - if (data->stop) - send_stop_abort(host, data); + if (!data->stop) { + dw_mci_request_end(host, host->mrq); + goto unlock; } - /* - * If err has non-zero, - * stop-abort command has been already issued. - */ - prev_state = state = STATE_SENDING_STOP; + if (host->mrq->sbc && !data->error) { + data->stop->error = 0; + dw_mci_request_end(host, host->mrq); + goto unlock; + } + prev_state = state = STATE_SENDING_STOP; + if (!data->error) + send_stop_cmd(host, data); /* fall through */ case STATE_SENDING_STOP: @@ -1359,19 +1151,9 @@ 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; - host->data = NULL; - - if (mrq->stop) - dw_mci_command_complete(host, mrq->stop); - else - host->cmd_status = 0; - - dw_mci_request_end(host, mrq); + dw_mci_command_complete(host, host->mrq->stop); + dw_mci_request_end(host, host->mrq); goto unlock; case STATE_DATA_ERROR: @@ -1915,6 +1697,7 @@ 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) { @@ -1953,10 +1736,11 @@ 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: - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; + mrq->stop->error = -ENOMEDIUM; break; } @@ -1979,10 +1763,23 @@ 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 */ - dw_mci_fifo_reset(host); + /* + * 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); + #ifdef CONFIG_MMC_DW_IDMAC - dw_mci_idmac_reset(host); + ctrl = mci_readl(host, BMOD); + /* Software reset of DMA */ + ctrl |= SDMMC_IDMAC_SWRESET; + mci_writel(host, BMOD, ctrl); #endif } @@ -2104,7 +1901,6 @@ 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); @@ -2120,14 +1916,8 @@ 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; - 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]; - } + mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); + mmc->f_max = host->bus_hz; if (host->pdata->get_ocr) mmc->ocr_avail = host->pdata->get_ocr(id); @@ -2174,6 +1964,9 @@ 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; @@ -2215,6 +2008,12 @@ 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: @@ -2275,57 +2074,36 @@ no_dma: return; } -static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) +static bool mci_wait_reset(struct device *dev, struct dw_mci *host) { unsigned long timeout = jiffies + msecs_to_jiffies(500); - u32 ctrl; + unsigned int ctrl; - ctrl = mci_readl(host, CTRL); - ctrl |= reset; - mci_writel(host, CTRL, ctrl); + mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_DMA_RESET)); /* wait till resets clear */ do { ctrl = mci_readl(host, CTRL); - if (!(ctrl & reset)) + if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_DMA_RESET))) return true; } while (time_before(jiffies, timeout)); - dev_err(host->dev, - "Timeout resetting block (ctrl reset %#x)\n", - ctrl & reset); + dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); 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, }, @@ -2380,15 +2158,6 @@ 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; } @@ -2452,15 +2221,6 @@ 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) { @@ -2527,7 +2287,7 @@ int dw_mci_probe(struct dw_mci *host) } /* Reset all blocks */ - if (!dw_mci_ctrl_all_reset(host)) + if (!mci_wait_reset(host->dev, host)) return -ENODEV; host->dma_ops = host->pdata->dma_ops; @@ -2557,8 +2317,8 @@ int dw_mci_probe(struct dw_mci *host) fifo_size = host->pdata->fifo_depth; } host->fifo_depth = fifo_size; - host->fifoth_val = - SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2); + host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | + ((fifo_size/2) << 0)); mci_writel(host, FIFOTH, host->fifoth_val); /* disable clock to CIU */ @@ -2696,6 +2456,23 @@ 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); @@ -2716,7 +2493,7 @@ int dw_mci_resume(struct dw_mci *host) } } - if (!dw_mci_ctrl_all_reset(host)) { + if (!mci_wait_reset(host->dev, host)) { ret = -ENODEV; return ret; } @@ -2724,15 +2501,8 @@ int dw_mci_resume(struct dw_mci *host) if (host->use_dma && host->dma_ops->init) host->dma_ops->init(host); - /* - * Restore the initial value at FIFOTH register - * And Invalidate the prev_blksz with zero - */ + /* Restore the old value at FIFOTH register */ 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 | @@ -2748,6 +2518,10 @@ 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 6bf24ab..81b2994 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -53,7 +53,6 @@ #define SDMMC_IDINTEN 0x090 #define SDMMC_DSCADDR 0x094 #define SDMMC_BUFADDR 0x098 -#define SDMMC_CDTHRCTL 0x100 #define SDMMC_DATA(x) (x) /* @@ -129,10 +128,6 @@ #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) @@ -147,8 +142,6 @@ #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) \ @@ -191,52 +184,6 @@ 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. @@ -256,7 +203,5 @@ 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 de2139c..6651633 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -880,6 +880,8 @@ 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; @@ -891,6 +893,8 @@ 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 f320579..c3785ed 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -62,7 +62,6 @@ static unsigned int fmax = 515633; * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock * @busy_detect: true if busy detection on dat0 is supported - * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply */ struct variant_data { unsigned int clkreg; @@ -77,7 +76,6 @@ struct variant_data { bool signal_direction; bool pwrreg_clkgate; bool busy_detect; - bool pwrreg_nopower; }; static struct variant_data variant_arm = { @@ -111,7 +109,6 @@ static struct variant_data variant_u300 = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, - .pwrreg_nopower = true, }; static struct variant_data variant_nomadik = { @@ -124,7 +121,6 @@ static struct variant_data variant_nomadik = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, - .pwrreg_nopower = true, }; static struct variant_data variant_ux500 = { @@ -139,7 +135,6 @@ static struct variant_data variant_ux500 = { .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, - .pwrreg_nopower = true, }; static struct variant_data variant_ux500v2 = { @@ -155,7 +150,6 @@ static struct variant_data variant_ux500v2 = { .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, - .pwrreg_nopower = true, }; static int mmci_card_busy(struct mmc_host *mmc) @@ -195,21 +189,6 @@ static int mmci_validate_data(struct mmci_host *host, return 0; } -static void mmci_reg_delay(struct mmci_host *host) -{ - /* - * According to the spec, at least three feedback clock cycles - * of max 52 MHz must pass between two writes to the MMCICLOCK reg. - * Three MCLK clock cycles must pass between two MMCIPOWER reg writes. - * Worst delay time during card init is at 100 kHz => 30 us. - * Worst delay time when up and running is at 25 MHz => 120 ns. - */ - if (host->cclk < 25000000) - udelay(30); - else - ndelay(120); -} - /* * This must be called with host->lock held */ @@ -1285,7 +1264,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mmci_set_clkreg(host, ios->clock); mmci_write_pwrreg(host, pwr); - mmci_reg_delay(host); spin_unlock_irqrestore(&host->lock, flags); @@ -1532,6 +1510,23 @@ static int mmci_probe(struct amba_device *dev, mmc->f_max = min(host->mclk, fmax); dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); + host->pinctrl = devm_pinctrl_get(&dev->dev); + if (IS_ERR(host->pinctrl)) { + ret = PTR_ERR(host->pinctrl); + goto clk_disable; + } + + host->pins_default = pinctrl_lookup_state(host->pinctrl, + PINCTRL_STATE_DEFAULT); + + /* enable pins to be muxed in and configured */ + if (!IS_ERR(host->pins_default)) { + ret = pinctrl_select_state(host->pinctrl, host->pins_default); + if (ret) + dev_warn(&dev->dev, "could not set default pins\n"); + } else + dev_warn(&dev->dev, "could not get default pinstate\n"); + /* Get regulators and the supported OCR mask */ mmc_regulator_get_supply(mmc); if (!mmc->ocr_avail) @@ -1730,67 +1725,41 @@ 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); - pm_runtime_get_sync(dev); - writel(0, host->base + MMCIMASK0); + + ret = mmc_suspend_host(mmc); + if (ret == 0) { + pm_runtime_get_sync(dev); + writel(0, host->base + MMCIMASK0); + } } - return 0; + return ret; } 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 0; + return ret; } #endif #ifdef CONFIG_PM_RUNTIME -static void mmci_save(struct mmci_host *host) -{ - unsigned long flags; - - if (host->variant->pwrreg_nopower) { - spin_lock_irqsave(&host->lock, flags); - - writel(0, host->base + MMCIMASK0); - writel(0, host->base + MMCIDATACTRL); - writel(0, host->base + MMCIPOWER); - writel(0, host->base + MMCICLOCK); - mmci_reg_delay(host); - - spin_unlock_irqrestore(&host->lock, flags); - } - -} - -static void mmci_restore(struct mmci_host *host) -{ - unsigned long flags; - - if (host->variant->pwrreg_nopower) { - spin_lock_irqsave(&host->lock, flags); - - writel(host->clk_reg, host->base + MMCICLOCK); - writel(host->datactrl_reg, host->base + MMCIDATACTRL); - writel(host->pwr_reg, host->base + MMCIPOWER); - writel(MCI_IRQENABLE, host->base + MMCIMASK0); - mmci_reg_delay(host); - - spin_unlock_irqrestore(&host->lock, flags); - } -} - static int mmci_runtime_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); @@ -1798,8 +1767,6 @@ static int mmci_runtime_suspend(struct device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); - pinctrl_pm_select_sleep_state(dev); - mmci_save(host); clk_disable_unprepare(host->clk); } @@ -1814,8 +1781,6 @@ static int mmci_runtime_resume(struct device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); clk_prepare_enable(host->clk); - mmci_restore(host); - pinctrl_pm_select_default_state(dev); } return 0; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 168bc72..69080fa 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -200,6 +200,10 @@ struct mmci_host { struct sg_mapping_iter sg_miter; unsigned int size; + /* pinctrl handles */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ struct dma_chan *dma_current; diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 9405ecd..b900de4 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1416,10 +1416,28 @@ 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); @@ -1427,11 +1445,14 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (host->stat_irq) disable_irq(host->stat_irq); - msmsdcc_writel(host, 0, MMCIMASK0); + if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) + rc = mmc_suspend_host(mmc); + if (!rc) + msmsdcc_writel(host, 0, MMCIMASK0); if (host->clks_on) msmsdcc_disable_clocks(host, 0); } - return 0; + return rc; } static int @@ -1446,6 +1467,8 @@ 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 45aa220..06c5b0b 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -655,7 +655,7 @@ static const struct mmc_host_ops mvsd_ops = { .enable_sdio_irq = mvsd_enable_sdio_irq, }; -static void +static void __init mv_conf_mbus_windows(struct mvsd_host *host, const struct mbus_dram_target_info *dram) { @@ -677,7 +677,7 @@ mv_conf_mbus_windows(struct mvsd_host *host, } } -static int mvsd_probe(struct platform_device *pdev) +static int __init mvsd_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mmc_host *mmc = NULL; @@ -775,9 +775,9 @@ static int mvsd_probe(struct platform_device *pdev) spin_lock_init(&host->lock); - host->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); + host->base = devm_request_and_ioremap(&pdev->dev, r); + if (!host->base) { + ret = -ENOMEM; goto out; } @@ -819,7 +819,7 @@ out: return ret; } -static int mvsd_remove(struct platform_device *pdev) +static int __exit mvsd_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); @@ -838,6 +838,33 @@ 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 */ } @@ -845,15 +872,16 @@ static const struct of_device_id mvsdio_dt_ids[] = { MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); static struct platform_driver mvsd_driver = { - .probe = mvsd_probe, - .remove = mvsd_remove, + .remove = __exit_p(mvsd_remove), + .suspend = mvsd_suspend, + .resume = mvsd_resume, .driver = { .name = DRIVER_NAME, .of_match_table = mvsdio_dt_ids, }, }; -module_platform_driver(mvsd_driver); +module_platform_driver_probe(mvsd_driver, mvsd_probe); /* maximum card clock frequency (default 50MHz) */ module_param(maxfreq, int, 0); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index f7199c8..c174c6a 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1250,20 +1250,28 @@ 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 0; + + return ret; } 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); - return 0; + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; } 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 50fc9df..e1fa3ef 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -724,9 +724,13 @@ 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 0; + + return ret; } static int mxs_mmc_resume(struct device *dev) @@ -734,9 +738,13 @@ 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); - return 0; + + ret = mmc_resume_host(mmc); + + return ret; } 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 98b6b6e..b94f38e 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -22,7 +22,6 @@ #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/timer.h> -#include <linux/of.h> #include <linux/omap-dma.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -91,6 +90,17 @@ #define OMAP_MMC_CMDTYPE_AC 2 #define OMAP_MMC_CMDTYPE_ADTC 3 +#define OMAP_DMA_MMC_TX 21 +#define OMAP_DMA_MMC_RX 22 +#define OMAP_DMA_MMC2_TX 54 +#define OMAP_DMA_MMC2_RX 55 + +#define OMAP24XX_DMA_MMC2_TX 47 +#define OMAP24XX_DMA_MMC2_RX 48 +#define OMAP24XX_DMA_MMC1_TX 61 +#define OMAP24XX_DMA_MMC1_RX 62 + + #define DRIVER_NAME "mmci-omap" /* Specifies how often in millisecs to poll for card status changes @@ -118,6 +128,7 @@ struct mmc_omap_slot { struct mmc_omap_host { int initialized; + int suspended; struct mmc_request * mrq; struct mmc_command * cmd; struct mmc_data * data; @@ -1320,7 +1331,7 @@ static int mmc_omap_probe(struct platform_device *pdev) struct mmc_omap_host *host = NULL; struct resource *res; dma_cap_mask_t mask; - unsigned sig = 0; + unsigned sig; int i, ret = 0; int irq; @@ -1330,7 +1341,7 @@ static int mmc_omap_probe(struct platform_device *pdev) } if (pdata->nr_slots == 0) { dev_err(&pdev->dev, "no slots\n"); - return -EPROBE_DEFER; + return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1397,20 +1408,19 @@ static int mmc_omap_probe(struct platform_device *pdev) host->dma_tx_burst = -1; host->dma_rx_burst = -1; - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (res) - sig = res->start; - host->dma_tx = dma_request_slave_channel_compat(mask, - omap_dma_filter_fn, &sig, &pdev->dev, "tx"); + if (mmc_omap2()) + sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX; + else + sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; + host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); if (!host->dma_tx) dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n", sig); - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (res) - sig = res->start; - host->dma_rx = dma_request_slave_channel_compat(mask, - omap_dma_filter_fn, &sig, &pdev->dev, "rx"); + if (mmc_omap2()) + sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; + else + sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; + host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); if (!host->dma_rx) dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n", sig); @@ -1503,20 +1513,64 @@ static int mmc_omap_remove(struct platform_device *pdev) return 0; } -#if IS_BUILTIN(CONFIG_OF) -static const struct of_device_id mmc_omap_match[] = { - { .compatible = "ti,omap2420-mmc", }, - { }, -}; +#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, - .of_match_table = of_match_ptr(mmc_omap_match), }, }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index dbd32ad..6ac63df 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -75,7 +75,6 @@ #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 @@ -120,8 +119,7 @@ BRR_EN | BWR_EN | TC_EN | CC_EN) #define MMC_AUTOSUSPEND_DELAY 100 -#define MMC_TIMEOUT_MS 20 /* 20 mSec */ -#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */ +#define MMC_TIMEOUT_MS 20 #define OMAP_MMC_MIN_CLOCK 400000 #define OMAP_MMC_MAX_CLOCK 52000000 #define DRIVER_NAME "omap_hsmmc" @@ -173,10 +171,6 @@ 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; @@ -189,6 +183,7 @@ struct omap_hsmmc_host { int use_reg; int req_in_progress; struct omap_hsmmc_next next_data; + struct omap_mmc_platform_data *pdata; }; @@ -498,8 +493,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 > CLKD_MAX) - dsor = CLKD_MAX; + if (dsor > 250) + dsor = 250; } return dsor; @@ -602,19 +597,24 @@ 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 (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) - return 1; + if (pdata->get_context_loss_count) { + context_loss = pdata->get_context_loss_count(host->dev); + if (context_loss < 0) + 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; + dev_dbg(mmc_dev(host->mmc), "context was %slost\n", + context_loss == host->context_loss ? "not " : ""); + if (host->context_loss == context_loss) + return 1; - host->context_loss++; + if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) + return 1; if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { if (host->power_mode != MMC_POWER_OFF && @@ -655,8 +655,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) omap_hsmmc_set_bus_mode(host); out: - dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n", - host->context_loss); + host->context_loss = context_loss; + + dev_dbg(mmc_dev(host->mmc), "context is restored\n"); return 0; } @@ -665,10 +666,15 @@ out: */ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) { - 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); + 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; + } } #else @@ -969,7 +975,8 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, unsigned long bit) { unsigned long i = 0; - unsigned long limit = MMC_TIMEOUT_US; + unsigned long limit = (loops_per_jiffy * + msecs_to_jiffies(MMC_TIMEOUT_MS)); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | bit); @@ -981,13 +988,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)) - udelay(1); + cpu_relax(); } i = 0; while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && (i++ < limit)) - udelay(1); + cpu_relax(); if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) dev_err(mmc_dev(host->mmc), @@ -1171,6 +1178,9 @@ 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) @@ -1625,9 +1635,18 @@ 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\n\nregs:\n", - mmc->index, host->context_loss); + 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; + } pm_runtime_get_sync(host->dev); @@ -1819,6 +1838,13 @@ 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) @@ -1848,7 +1874,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 && res->start == 0x4809c000) + if (host->dev->of_node && host->mapbase == 0x4809c000) host->pbias_disable = 1; host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); @@ -2093,12 +2119,23 @@ 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); @@ -2108,19 +2145,23 @@ static int omap_hsmmc_suspend(struct device *dev) if (host->dbclk) clk_disable_unprepare(host->dbclk); - +err: pm_runtime_put_sync(host->dev); - return 0; + return ret; } /* 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) @@ -2131,9 +2172,16 @@ 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 0; + + return ret; + } #else diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 32fe113..1956a3d 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -880,6 +880,35 @@ 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, @@ -887,6 +916,9 @@ 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 c46feda..375a880e 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_card_uhs(card); + int uhs = mmc_sd_card_uhs(card); int read = (data->flags & MMC_DATA_READ) ? 1 : 0; u8 cfg2, trans_mode; int err; @@ -1197,6 +1197,37 @@ 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; @@ -1336,6 +1367,8 @@ 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 2fce5ea..8d6794c 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1949,10 +1949,39 @@ 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-acpi.c b/drivers/mmc/host/sdhci-acpi.c index ef19874..cdd4ce0 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -310,9 +310,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev) dma_mask = DMA_BIT_MASK(32); } - err = dma_coerce_mask_and_coherent(dev, dma_mask); - if (err) - goto err_free; + dev->dma_mask = &dev->coherent_dma_mask; + dev->coherent_dma_mask = dma_mask; } if (c->slot) { diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 7a190fe..85472d3 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -316,7 +316,19 @@ err_pltfm_free: static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev) { - return sdhci_pltfm_unregister(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; } 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 f6d8d67..36fa2df 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -178,7 +178,13 @@ err: static int bcm2835_sdhci_remove(struct platform_device *pdev) { - return sdhci_pltfm_unregister(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; } 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 461a4c3..abc8cf0 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -34,40 +34,12 @@ /* 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 */ @@ -94,60 +66,21 @@ * 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 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, +#define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1) + +enum imx_esdhc_type { + IMX25_ESDHC, + IMX35_ESDHC, + IMX51_ESDHC, + IMX53_ESDHC, + IMX6Q_USDHC, }; 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; @@ -157,20 +90,25 @@ 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 = (kernel_ulong_t) &esdhc_imx25_data, + .driver_data = IMX25_ESDHC, }, { .name = "sdhci-esdhc-imx35", - .driver_data = (kernel_ulong_t) &esdhc_imx35_data, + .driver_data = IMX35_ESDHC, }, { .name = "sdhci-esdhc-imx51", - .driver_data = (kernel_ulong_t) &esdhc_imx51_data, + .driver_data = IMX51_ESDHC, + }, { + .name = "sdhci-esdhc-imx53", + .driver_data = IMX53_ESDHC, + }, { + .name = "sdhci-usdhc-imx6q", + .driver_data = IMX6Q_USDHC, }, { /* sentinel */ } @@ -178,34 +116,38 @@ 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 = &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, }, + { .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], }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); static inline int is_imx25_esdhc(struct pltfm_imx_data *data) { - return data->socdata == &esdhc_imx25_data; + return data->devtype == IMX25_ESDHC; } -static inline int is_imx53_esdhc(struct pltfm_imx_data *data) +static inline int is_imx35_esdhc(struct pltfm_imx_data *data) { - return data->socdata == &esdhc_imx53_data; + return data->devtype == IMX35_ESDHC; } -static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) +static inline int is_imx51_esdhc(struct pltfm_imx_data *data) +{ + return data->devtype == IMX51_ESDHC; +} + +static inline int is_imx53_esdhc(struct pltfm_imx_data *data) { - return data->socdata == &usdhc_imx6q_data; + return data->devtype == IMX53_ESDHC; } -static inline int esdhc_is_usdhc(struct pltfm_imx_data *data) +static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) { - return !!(data->socdata->flags & ESDHC_FLAG_USDHC); + return data->devtype == IMX6Q_USDHC; } static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) @@ -222,21 +164,7 @@ 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 @@ -250,25 +178,6 @@ 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; @@ -315,7 +224,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) } } - if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) + if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (reg == SDHCI_INT_STATUS) && (val & SDHCI_INT_DATA_END))) { u32 v; @@ -347,12 +256,10 @@ 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 (esdhc_is_usdhc(imx_data)) { + if (is_imx6q_usdhc(imx_data)) { /* * The usdhc register returns a wrong host version. * Correct it here. @@ -361,30 +268,6 @@ 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); } @@ -392,59 +275,10 @@ 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->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) + if ((imx_data->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)) { @@ -454,7 +288,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(v, host->ioaddr + ESDHC_VENDOR_SPEC); } - if (esdhc_is_usdhc(imx_data)) { + if (is_imx6q_usdhc(imx_data)) { u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); /* Swap AC23 bit */ if (val & SDHCI_TRNS_AUTO_CMD23) { @@ -476,10 +310,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->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) + (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) imx_data->multiblock_status = MULTIBLK_IN_PROCESS; - if (esdhc_is_usdhc(imx_data)) + if (is_imx6q_usdhc(imx_data)) writel(val << 16, host->ioaddr + SDHCI_TRANSFER_MODE); else @@ -545,10 +379,8 @@ 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 (esdhc_is_usdhc(imx_data)) { + if (is_imx6q_usdhc(imx_data)) writel(0, host->ioaddr + ESDHC_MIX_CTRL); - imx_data->is_ddr = 0; - } } } @@ -577,60 +409,8 @@ 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++; - 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; + esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk)); } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -674,192 +454,7 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) return 0; } -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 = { +static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, .write_l = esdhc_writel_le, @@ -870,7 +465,6 @@ static 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 = { @@ -912,14 +506,6 @@ 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 @@ -953,8 +539,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto free_sdhci; } - imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *) - pdev->id_entry->driver_data; + if (of_id) + pdev->id_entry = of_id->data; + imx_data->devtype = pdev->id_entry->driver_data; pltfm_host->priv = imx_data; imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); @@ -981,39 +568,29 @@ 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(&pdev->dev); + imx_data->pinctrl = devm_pinctrl_get_select_default(&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 (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207) + if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) /* 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 (esdhc_is_usdhc(imx_data)) { + if (is_imx6q_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) { @@ -1073,23 +650,6 @@ 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 3abd1e7..7725160 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -57,4 +57,58 @@ #define ESDHC_HOST_CONTROL_RES 0x01 #define ESDHC_VOL_SEL 0x04 +static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock, + unsigned int host_clock) +{ + u32 timeout; + 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); + + /* Wait max 20 ms */ + timeout = 20; + while (!(sdhci_readl(host, ESDHCI_PRESENT_STATE) & ESDHC_CLK_STABLE)) { + if (timeout == 0) { + pr_err("%s: Internal clock never " + "stabilised.\n", mmc_hostname(host->mmc)); + return; + } + timeout--; + mdelay(1); + } + + temp |= ESDHC_CLOCK_CRDEN; + 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 3af4587..0f9f36b 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -336,14 +336,6 @@ 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) { - u32 timeout; - 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) @@ -352,46 +344,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) clock -= 5000000; } - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN - | ESDHC_CLOCK_MASK | ESDHC_CLOCK_CRDEN); - 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); - - /* Wait max 20 ms */ - timeout = 20; - while (!(sdhci_readl(host, ESDHCI_PRESENT_STATE) & ESDHC_CLK_STABLE)) { - if (timeout == 0) { - pr_err("%s: Internal clock never " - "stabilised.\n", mmc_hostname(host->mmc)); - return; - } - timeout--; - mdelay(1); - } - - temp |= ESDHC_CLOCK_CRDEN; - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - mdelay(1); -out: - host->clock = clock; + /* Set the clock */ + esdhc_set_clock(host, clock, host->max_clk); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 8f75381..d7d6bc8 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -37,12 +37,6 @@ #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 @@ -362,28 +356,6 @@ 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 @@ -967,54 +939,6 @@ 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 bfb3d8e..c76dab0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -49,6 +49,7 @@ 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); @@ -989,7 +990,7 @@ static void sdhci_finish_data(struct sdhci_host *host) tasklet_schedule(&host->finish_tasklet); } -void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) +static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; u32 mask; @@ -1068,7 +1069,6 @@ 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) { @@ -1451,8 +1451,7 @@ 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) && - !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) + (ios->power_mode == MMC_POWER_UP)) sdhci_enable_preset_value(host, false); sdhci_set_clock(host, ios->clock); @@ -1892,14 +1891,6 @@ 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); /* @@ -2006,7 +1997,6 @@ 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" @@ -2521,14 +2511,6 @@ 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: @@ -2584,6 +2566,8 @@ 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); @@ -2595,6 +2579,19 @@ 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); @@ -2602,14 +2599,14 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_enable_irq_wakeups(host); enable_irq_wake(host->irq); } - return 0; + return ret; } EXPORT_SYMBOL_GPL(sdhci_suspend_host); int sdhci_resume_host(struct sdhci_host *host) { - int ret = 0; + int ret; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) @@ -2638,6 +2635,7 @@ 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 4e68539..096b203 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -290,7 +290,6 @@ 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); @@ -399,8 +398,6 @@ extern void sdhci_reset(struct sdhci_host *host, u8 mask); 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 b7e3057..50adbd1 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -516,7 +516,9 @@ 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; } @@ -525,6 +527,7 @@ 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 d032b08..36629a0 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_prepare_enable(host->hclk); + int ret = clk_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_unprepare(host->hclk); + clk_disable(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_unprepare(host->hclk); + clk_disable(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_unprepare(host->hclk); + clk_disable(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_prepare_enable(host->hclk); + clk_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_unprepare(host->hclk); + clk_disable(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1538,21 +1538,28 @@ static int sh_mmcif_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static int sh_mmcif_suspend(struct device *dev) { struct sh_mmcif_host *host = dev_get_drvdata(dev); + int ret = mmc_suspend_host(host->mmc); - sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + if (!ret) + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - return 0; + return ret; } static int sh_mmcif_resume(struct device *dev) { - return 0; + struct sh_mmcif_host *host = dev_get_drvdata(dev); + + return mmc_resume_host(host->mmc); } -#endif +#else +#define sh_mmcif_suspend NULL +#define sh_mmcif_resume NULL +#endif /* CONFIG_PM */ static const struct of_device_id mmcif_of_match[] = { { .compatible = "renesas,sh-mmcif" }, @@ -1561,7 +1568,8 @@ 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 = { - SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume) + .suspend = sh_mmcif_suspend, + .resume = 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 d1760eb..43d9628 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 0; + return mmc_suspend_host(tifm_get_drvdata(sock)); } static int tifm_sd_resume(struct tifm_dev *sock) @@ -1044,6 +1044,8 @@ 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 f3b2d8c..b380225 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1145,9 +1145,12 @@ 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); - tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); - return 0; + if (!ret) + tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); + + return ret; } EXPORT_SYMBOL(tmio_mmc_host_suspend); @@ -1160,7 +1163,7 @@ int tmio_mmc_host_resume(struct device *dev) /* The MMC core will perform the complete set up */ host->resuming = true; - return 0; + return mmc_resume_host(mmc); } EXPORT_SYMBOL(tmio_mmc_host_resume); diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 63fac78..4f84586 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1269,18 +1269,21 @@ 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 0; + return ret; } static int via_sd_resume(struct pci_dev *pcidev) @@ -1313,6 +1316,8 @@ 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 4262296..e9028ad 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2392,12 +2392,26 @@ static void vub300_disconnect(struct usb_interface *interface) #ifdef CONFIG_PM static int vub300_suspend(struct usb_interface *intf, pm_message_t message) { - return 0; + 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; + } } static int vub300_resume(struct usb_interface *intf) { - return 0; + 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; + } } #else #define vub300_suspend NULL diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 1defd5e..e954b77 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1814,11 +1814,28 @@ 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; @@ -1827,7 +1844,12 @@ 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; } @@ -1850,8 +1872,7 @@ static int wbsd_platform_resume(struct platform_device *dev) */ mdelay(5); - wbsd_init_device(host); - return 0; + return wbsd_resume(host); } #ifdef CONFIG_PNP @@ -1859,12 +1880,16 @@ 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"); - return 0; + + host = mmc_priv(mmc); + + return wbsd_suspend(host, state); } static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) @@ -1897,8 +1922,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) */ mdelay(5); - wbsd_init_device(host); - return 0; + return wbsd_resume(host); } #endif /* CONFIG_PNP */ diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index e902ed7..34231d5 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -212,14 +212,28 @@ struct wmt_mci_priv { static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) { - 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); + 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); + } + } } static void wmt_mci_read_response(struct mmc_host *mmc) @@ -925,23 +939,28 @@ 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); - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + - SDMMC_BUSMODE); + ret = mmc_suspend_host(mmc); - reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); - writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); + if (!ret) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); - writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); - writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); - clk_disable(priv->clk_sdmmc); - return 0; + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + clk_disable(priv->clk_sdmmc); + } + return ret; } static int wmt_mci_resume(struct device *dev) @@ -950,6 +969,7 @@ 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); @@ -967,9 +987,10 @@ 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 0; + return ret; } static const struct dev_pm_ops wmt_mci_pm = { |