diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/card/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mmc/card/block.c | 18 | ||||
-rw-r--r-- | drivers/mmc/core/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 94 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 2 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 10 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 4 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 84 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 148 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 13 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/mmc/core/sdio_io.c | 0 | ||||
-rw-r--r-- | drivers/mmc/host/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 26 | ||||
-rwxr-xr-x | drivers/mmc/host/sdhci.c | 69 |
14 files changed, 426 insertions, 74 deletions
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 3b1f783bf924..ebb4afe6c702 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE If unsure, say Y here. +config MMC_BLOCK_DEFERRED_RESUME + bool "Deferr MMC layer resume until I/O is requested" + depends on MMC_BLOCK + default n + help + Say Y here to enable deferred MMC resume until I/O + is requested. This will reduce overall resume latency and + save power when theres an SD card inserted but not being used. + config SDIO_UART tristate "SDIO UART/GPS class support" help diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c97ada2af756..520414299399 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1179,12 +1179,22 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 0; } +static int +mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card); + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { int ret; struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + if (mmc_bus_needs_resume(card->host)) { + mmc_resume_bus(card->host); + mmc_blk_set_blksize(md, card); + } +#endif + if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ mmc_claim_host(card->host); @@ -1529,6 +1539,9 @@ static int mmc_blk_probe(struct mmc_card *card) mmc_set_drvdata(card, md); mmc_fixup_device(card, blk_fixups); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 1); +#endif if (mmc_add_disk(md)) goto out; @@ -1554,6 +1567,9 @@ static void mmc_blk_remove(struct mmc_card *card) mmc_release_host(card->host); mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 0); +#endif } #ifdef CONFIG_PM_SLEEP @@ -1577,7 +1593,9 @@ static int mmc_blk_resume(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { +#ifndef CONFIG_MMC_BLOCK_DEFERRED_RESUME mmc_blk_set_blksize(md, card); +#endif /* * Resume involves the card going into idle state, diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index ef103871517f..85c2e1acd156 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -27,3 +27,20 @@ config MMC_CLKGATE support handling this in order for it to be of any use. If unsure, say N. + +config MMC_EMBEDDED_SDIO + boolean "MMC embedded SDIO device support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, support will be added for embedded SDIO + devices which do not contain the necessary enumeration + support in hardware to be properly detected. + +config MMC_PARANOID_SD_INIT + bool "Enable paranoid SD card initialization (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, the MMC layer will be extra paranoid + about re-trying SD init requests. This can be a useful + work-around for buggy controllers and hardware. Enable + if you are experiencing issues with SD detection. diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index cdc68bc7165b..c7ec3620d279 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,6 +23,7 @@ #include <linux/log2.h> #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> +#include <linux/wakelock.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -1228,6 +1229,36 @@ static inline void mmc_bus_put(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); } +int mmc_resume_bus(struct mmc_host *host) +{ + unsigned long flags; + + if (!mmc_bus_needs_resume(host)) + return -EINVAL; + + printk("%s: Starting deferred resume\n", mmc_hostname(host)); + spin_lock_irqsave(&host->lock, flags); + host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME; + host->rescan_disable = 0; + spin_unlock_irqrestore(&host->lock, flags); + + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + mmc_power_up(host); + BUG_ON(!host->bus_ops->resume); + host->bus_ops->resume(host); + } + + if (host->bus_ops->detect && !host->bus_dead) + host->bus_ops->detect(host); + + mmc_bus_put(host); + printk("%s: Deferred resume completed\n", mmc_hostname(host)); + return 0; +} + +EXPORT_SYMBOL(mmc_resume_bus); + /* * Assign a mmc bus handler to a host. Only one bus handler may control a * host at any given time. @@ -1293,6 +1324,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) spin_unlock_irqrestore(&host->lock, flags); #endif + wake_lock(&host->detect_wake_lock); mmc_schedule_delayed_work(&host->detect, delay); } @@ -1775,6 +1807,7 @@ void mmc_rescan(struct work_struct *work) struct mmc_host *host = container_of(work, struct mmc_host, detect.work); int i; + bool extend_wakelock = false; if (host->rescan_disable) return; @@ -1789,6 +1822,12 @@ void mmc_rescan(struct work_struct *work) && !(host->caps & MMC_CAP_NONREMOVABLE)) host->bus_ops->detect(host); + /* If the card was removed the bus will be marked + * as dead - extend the wakelock so userspace + * can respond */ + if (host->bus_dead) + extend_wakelock = 1; + /* * Let mmc_bus_put() free the bus/bus_ops if we've found that * the card is no longer present. @@ -1813,16 +1852,24 @@ void mmc_rescan(struct work_struct *work) mmc_claim_host(host); for (i = 0; i < ARRAY_SIZE(freqs); i++) { - if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) + if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) { + extend_wakelock = true; break; + } if (freqs[i] <= host->f_min) break; } mmc_release_host(host); out: - if (host->caps & MMC_CAP_NEEDS_POLL) + if (extend_wakelock) + wake_lock_timeout(&host->detect_wake_lock, HZ / 2); + else + wake_unlock(&host->detect_wake_lock); + if (host->caps & MMC_CAP_NEEDS_POLL) { + wake_lock(&host->detect_wake_lock); mmc_schedule_delayed_work(&host->detect, HZ); + } } void mmc_start_host(struct mmc_host *host) @@ -1842,7 +1889,8 @@ void mmc_stop_host(struct mmc_host *host) if (host->caps & MMC_CAP_DISABLE) cancel_delayed_work(&host->disable); - cancel_delayed_work_sync(&host->detect); + if (cancel_delayed_work_sync(&host->detect)) + wake_unlock(&host->detect_wake_lock); mmc_flush_scheduled_work(); /* clear pm flags now and let card drivers set them as needed */ @@ -1959,9 +2007,13 @@ int mmc_suspend_host(struct mmc_host *host) { int err = 0; + if (mmc_bus_needs_resume(host)) + return 0; + if (host->caps & MMC_CAP_DISABLE) cancel_delayed_work(&host->disable); - cancel_delayed_work(&host->detect); + if (cancel_delayed_work(&host->detect)) + wake_unlock(&host->detect_wake_lock); mmc_flush_scheduled_work(); mmc_bus_get(host); @@ -1982,6 +2034,7 @@ int mmc_suspend_host(struct mmc_host *host) host->pm_flags = 0; err = 0; } + flush_delayed_work(&host->disable); } mmc_bus_put(host); @@ -2002,6 +2055,12 @@ int mmc_resume_host(struct mmc_host *host) int err = 0; mmc_bus_get(host); + if (mmc_bus_manual_resume(host)) { + host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME; + mmc_bus_put(host); + return 0; + } + if (host->bus_ops && !host->bus_dead) { if (!mmc_card_keep_power(host)) { mmc_power_up(host); @@ -2052,9 +2111,14 @@ int mmc_pm_notify(struct notifier_block *notify_block, case PM_SUSPEND_PREPARE: spin_lock_irqsave(&host->lock, flags); + if (mmc_bus_needs_resume(host)) { + spin_unlock_irqrestore(&host->lock, flags); + break; + } host->rescan_disable = 1; spin_unlock_irqrestore(&host->lock, flags); - cancel_delayed_work_sync(&host->detect); + if (cancel_delayed_work_sync(&host->detect)) + wake_unlock(&host->detect_wake_lock); if (!host->bus_ops || host->bus_ops->suspend) break; @@ -2075,6 +2139,10 @@ int mmc_pm_notify(struct notifier_block *notify_block, case PM_POST_RESTORE: spin_lock_irqsave(&host->lock, flags); + if (mmc_bus_manual_resume(host)) { + spin_unlock_irqrestore(&host->lock, flags); + break; + } host->rescan_disable = 0; spin_unlock_irqrestore(&host->lock, flags); mmc_detect_change(host, 0); @@ -2085,6 +2153,22 @@ int mmc_pm_notify(struct notifier_block *notify_block, } #endif +#ifdef CONFIG_MMC_EMBEDDED_SDIO +void mmc_set_embedded_sdio_data(struct mmc_host *host, + struct sdio_cis *cis, + struct sdio_cccr *cccr, + struct sdio_embedded_func *funcs, + int num_funcs) +{ + host->embedded_sdio_data.cis = cis; + host->embedded_sdio_data.cccr = cccr; + host->embedded_sdio_data.funcs = funcs; + host->embedded_sdio_data.num_funcs = num_funcs; +} + +EXPORT_SYMBOL(mmc_set_embedded_sdio_data); +#endif + static int __init mmc_init(void) { int ret; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 14664f1fb16f..afed70f7d0cc 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -33,6 +33,8 @@ void mmc_init_erase(struct mmc_card *card); void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); +void mmc_set_tuning(struct mmc_host *host, unsigned int tuning); +void mmc_finish_tuning(struct mmc_host *host); void mmc_gate_clock(struct mmc_host *host); void mmc_ungate_clock(struct mmc_host *host); void mmc_set_ungated(struct mmc_host *host); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 793d0a0dad8d..e09f0a7eb652 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -284,6 +284,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); + wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND, + kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host))); INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); #ifdef CONFIG_PM @@ -336,7 +338,8 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); - register_pm_notifier(&host->pm_notify); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + register_pm_notifier(&host->pm_notify); return 0; } @@ -353,7 +356,9 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - unregister_pm_notifier(&host->pm_notify); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + unregister_pm_notifier(&host->pm_notify); + mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS @@ -380,6 +385,7 @@ void mmc_free_host(struct mmc_host *host) spin_lock(&mmc_host_lock); idr_remove(&mmc_host_idr, host->index); spin_unlock(&mmc_host_lock); + wake_lock_destroy(&host->detect_wake_lock); put_device(&host->class_dev); } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ce5e63c61b51..a2f4b13bce21 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -887,8 +887,8 @@ static ssize_t mmc_boot_info_show(struct device *dev, bus_width[width]); } -DEVICE_ATTR(boot_config, S_IWUGO, NULL, setup_boot_partitions); -DEVICE_ATTR(boot_bus_config, S_IWUGO, NULL, setup_boot_bus); +DEVICE_ATTR(boot_config, S_IWUSR | S_IWGRP, NULL, setup_boot_partitions); +DEVICE_ATTR(boot_bus_config, S_IWUSR | S_IWGRP, NULL, setup_boot_bus); DEVICE_ATTR(boot_info, S_IRUGO, mmc_boot_info_show, NULL); MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", card->ext_csd.enhanced_area_offset); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 716c0a966eba..074ded0f4bae 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -765,6 +765,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit) { int err; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif if (!reinit) { /* @@ -791,7 +794,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, /* * Fetch switch information from card. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + for (retries = 1; retries <= 3; retries++) { + err = mmc_read_switch(card); + if (!err) { + if (retries > 1) { + printk(KERN_WARNING + "%s: recovered\n", + mmc_hostname(host)); + } + break; + } else { + printk(KERN_WARNING + "%s: read switch failed (attempt %d)\n", + mmc_hostname(host), retries); + } + } +#else err = mmc_read_switch(card); +#endif + if (err) return err; } @@ -990,7 +1012,10 @@ static void mmc_sd_remove(struct mmc_host *host) */ static void mmc_sd_detect(struct mmc_host *host) { - int err; + int err = 0; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries = 5; +#endif BUG_ON(!host); BUG_ON(!host->card); @@ -1000,8 +1025,23 @@ static void mmc_sd_detect(struct mmc_host *host) /* * Just check if our card has been removed. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + while(retries) { + err = mmc_send_status(host->card, NULL); + if (err) { + retries--; + udelay(5); + continue; + } + break; + } + if (!retries) { + printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n", + __func__, mmc_hostname(host), err); + } +#else err = mmc_send_status(host->card, NULL); - +#endif mmc_release_host(host); if (err) { @@ -1040,12 +1080,31 @@ static int mmc_sd_suspend(struct mmc_host *host) static int mmc_sd_resume(struct mmc_host *host) { int err; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); +#ifdef CONFIG_MMC_PARANOID_SD_INIT + retries = 5; + while (retries) { + err = mmc_sd_init_card(host, host->ocr, host->card); + + if (err) { + printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n", + mmc_hostname(host), err, retries); + mdelay(5); + retries--; + continue; + } + break; + } +#else err = mmc_sd_init_card(host, host->ocr, host->card); +#endif mmc_release_host(host); return err; @@ -1097,6 +1156,9 @@ int mmc_attach_sd(struct mmc_host *host) { int err; u32 ocr; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif BUG_ON(!host); WARN_ON(!host->claimed); @@ -1161,9 +1223,27 @@ int mmc_attach_sd(struct mmc_host *host) /* * Detect and init the card. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + retries = 5; + while (retries) { + err = mmc_sd_init_card(host, host->ocr, NULL); + if (err) { + retries--; + continue; + } + break; + } + + if (!retries) { + printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n", + mmc_hostname(host), err); + goto err; + } +#else err = mmc_sd_init_card(host, host->ocr, NULL); if (err) goto err; +#endif mmc_release_host(host); err = mmc_add_card(host->card); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index cc3736bedb2b..9db650f00734 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -27,6 +27,10 @@ #include "sdio_ops.h" #include "sdio_cis.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include <linux/mmc/sdio_ids.h> +#endif + static int sdio_read_fbr(struct sdio_func *func) { int ret; @@ -449,19 +453,35 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto finish; } - /* - * Read the common registers. - */ - err = sdio_read_cccr(card); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cccr) + memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr)); + else { +#endif + /* + * Read the common registers. + */ + err = sdio_read_cccr(card); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif - /* - * Read the common CIS tuples. - */ - err = sdio_read_common_cis(card); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cis) + memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis)); + else { +#endif + /* + * Read the common CIS tuples. + */ + err = sdio_read_common_cis(card); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif if (oldcard) { int same = (card->cis.vendor == oldcard->cis.vendor && @@ -827,14 +847,36 @@ int mmc_attach_sdio(struct mmc_host *host) funcs = (ocr & 0x70000000) >> 28; card->sdio_funcs = 0; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) + card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs; +#endif + /* * Initialize (but don't add) all present functions. */ for (i = 0; i < funcs; i++, card->sdio_funcs++) { - err = sdio_init_func(host->card, i + 1); - if (err) - goto remove; - +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) { + struct sdio_func *tmp; + + tmp = sdio_alloc_func(host->card); + if (IS_ERR(tmp)) + goto remove; + tmp->num = (i + 1); + card->sdio_func[i] = tmp; + tmp->class = host->embedded_sdio_data.funcs[i].f_class; + tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize; + tmp->vendor = card->cis.vendor; + tmp->device = card->cis.device; + } else { +#endif + err = sdio_init_func(host->card, i + 1); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif /* * Enable Runtime PM for this func (if supported) */ @@ -882,3 +924,77 @@ err: return err; } +int sdio_reset_comm(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 ocr; + int err; + + printk("%s():\n", __func__); + mmc_claim_host(host); + + mmc_go_idle(host); + + mmc_set_clock(host, host->f_min); + + err = mmc_send_io_op_cond(host, 0, &ocr); + if (err) + goto err; + + host->ocr = mmc_select_voltage(host, ocr); + if (!host->ocr) { + err = -EINVAL; + goto err; + } + + err = mmc_send_io_op_cond(host, host->ocr, &ocr); + if (err) + goto err; + + if (mmc_host_is_spi(host)) { + err = mmc_spi_set_crc(host, use_spi_crc); + if (err) + goto err; + } + + if (!mmc_host_is_spi(host)) { + err = mmc_send_relative_addr(host, &card->rca); + if (err) + goto err; + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + } + if (!mmc_host_is_spi(host)) { + err = mmc_select_card(card); + if (err) + goto err; + } + + /* + * Switch to high-speed (if supported). + */ + err = sdio_enable_hs(card); + if (err > 0) + mmc_sd_go_highspeed(card); + else if (err) + goto err; + + /* + * Change to the card's maximum speed. + */ + mmc_set_clock(host, mmc_sdio_get_max_clock(card)); + + err = sdio_enable_4bit_bus(card); + if (err > 0) + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + else if (err) + goto err; + + mmc_release_host(host); + return 0; +err: + printk("%s: Error resetting SDIO communications (%d)\n", + mmc_hostname(host), err); + mmc_release_host(host); + return err; +} +EXPORT_SYMBOL(sdio_reset_comm); diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index d2565df8a7fb..52429a98201b 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -23,6 +23,10 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include <linux/mmc/host.h> +#endif + /* show configuration fields */ #define sdio_config_attr(field, format_string) \ static ssize_t \ @@ -260,7 +264,14 @@ static void sdio_release_func(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); - sdio_free_func_cis(func); +#ifdef CONFIG_MMC_EMBEDDED_SDIO + /* + * If this device is embedded then we never allocated + * cis tables for this func + */ + if (!func->card->host->embedded_sdio_data.funcs) +#endif + sdio_free_func_cis(func); if (func->info) kfree(func->info); diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 0f687cdeb064..0f687cdeb064 100644..100755 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 64aac7898334..d86cb895a112 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -579,3 +579,9 @@ config MMC_USHC Note: These controllers only support SDIO cards and do not support MMC or SD memory cards. +config SDHCI_USE_LEDS_CLASS + tristate "Support LEDs indicators for SDHC HOST" + depends on MMC_SDHCI && LEDS_CLASS + help + This selects support for SDHCI HOST Controllers use LED Class + to control the status of LEDs diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 35fd825f4709..fd7046808ed4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -34,6 +34,7 @@ #define SDHCI_VENDOR_SPEC 0xC0 #define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002 +#define SDHCI_MIX_CTRL_AC12EN (1 << 2) #define SDHCI_MIX_CTRL_AC23EN (1 << 7) #define SDHCI_MIX_CTRL_EXE_TUNE (1 << 22) #define SDHCI_MIX_CTRL_SMPCLK_SEL (1 << 23) @@ -291,9 +292,12 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; u32 data; + struct esdhc_platform_data *boarddata + = host->mmc->parent->platform_data; if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE))) { - if (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP) + if ((boarddata->always_present) || + (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP)) /* * these interrupts won't work with a custom * card_detect gpio (only applied to mx25/35) @@ -556,9 +560,12 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } imx_data->scratchpad = val; - if (val & SDHCI_TRNS_AUTO_CMD23) + if (cpu_is_mx6() && (val & SDHCI_TRNS_AUTO_CMD23)) imx_data->scratchpad |= SDHCI_MIX_CTRL_AC23EN; + if (cpu_is_mx5() && (val & SDHCI_TRNS_AUTO_CMD12)) + imx_data->scratchpad |= SDHCI_MIX_CTRL_AC12EN; + return; case SDHCI_COMMAND: if ((host->cmd->opcode == MMC_STOP_TRANSMISSION || @@ -782,9 +789,6 @@ static irqreturn_t cd_irq(int irq, void *data) imx_data->scratchpad &= ~SDHCI_MIX_CTRL_SMPCLK_SEL; } - esdhc_reset(sdhost); - mdelay(1); - tasklet_schedule(&sdhost->card_tasklet); return IRQ_HANDLED; }; @@ -823,13 +827,13 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd /* * on mx6dl TO 1.1, ADMA can work when ahb bus frequency is low, - * like 24Mhz. + * like 24Mhz. MX53 does have working ADMA. */ - if (mx6dl_revision() >= IMX_CHIP_REVISION_1_1) + if (mx6dl_revision() >= IMX_CHIP_REVISION_1_1 || cpu_is_mx53()) host->quirks &= ~SDHCI_QUIRK_BROKEN_ADMA; if (cpu_is_mx6()) - host->quirks2 |= SDHCI_QUIRK_BROKEN_AUTO_CMD23, + host->quirks2 |= SDHCI_QUIRK_BROKEN_AUTO_CMD23; /* write_protect can't be routed to controller, use gpio */ sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro; @@ -864,6 +868,8 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd || boarddata->cd_type == ESDHC_CD_NONE || boarddata->cd_type == ESDHC_CD_GPIO) host->mmc->caps &= ~MMC_CAP_NONREMOVABLE; + if (boarddata->runtime_pm) + host->mmc->caps |= MMC_CAP_POWER_OFF_CARD; if (cpu_is_mx6()) { host->mmc->caps |= MMC_CAP_1_8V_DDR; host->tuning_min = SDHCI_TUNE_CTRL_MIN; @@ -915,10 +921,6 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; } -#ifdef CONFIG_PM_RUNTIME - host->mmc->caps |= MMC_CAP_POWER_OFF_CARD; -#endif - if (host->clk_mgr_en) clk_disable(pltfm_host->clk); return 0; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d34486e8c9f4..1653d53a2744 100755 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -73,6 +73,15 @@ static inline bool sdhci_is_sdio_attached(struct sdhci_host *host) return false; } +static bool sdhci_can_gate_clk(struct sdhci_host *host) +{ + if (!sdhci_is_sdio_attached(host)) + return true; + if (host->power_mode == MMC_POWER_OFF) + return true; + return false; +} + static void sdhci_enable_clk(struct sdhci_host *host) { if (host->clk_mgr_en) { @@ -84,7 +93,7 @@ static void sdhci_enable_clk(struct sdhci_host *host) static void sdhci_disable_clk(struct sdhci_host *host, int delay) { - if (host->clk_mgr_en) { + if (host->clk_mgr_en && sdhci_can_gate_clk(host)) { if (delay == 0 && !in_interrupt()) { if (host->ops->platform_clk_ctrl && host->clk_status) host->ops->platform_clk_ctrl(host, false); @@ -282,7 +291,7 @@ static void sdhci_deactivate_led(struct sdhci_host *host) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } -#ifdef SDHCI_USE_LEDS_CLASS +#ifdef CONFIG_SDHCI_USE_LEDS_CLASS static void sdhci_led_control(struct led_classdev *led, enum led_brightness brightness) { @@ -298,8 +307,7 @@ static void sdhci_led_control(struct led_classdev *led, sdhci_activate_led(host); spin_unlock_irqrestore(&host->lock, flags); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, CLK_TIMEOUT); + sdhci_disable_clk(host, CLK_TIMEOUT); } #endif @@ -1089,7 +1097,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk = 0; unsigned long timeout; - if (clock == host->clock) + if (clock && clock == host->clock) return; if (host->ops->set_clock) { @@ -1257,7 +1265,7 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq != NULL); -#ifndef SDHCI_USE_LEDS_CLASS +#ifndef CONFIG_SDHCI_USE_LEDS_CLASS sdhci_activate_led(host); #endif @@ -1322,6 +1330,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host = mmc_priv(mmc); + host->power_mode = ios->power_mode; + sdhci_enable_clk(host); spin_lock_irqsave(&host->lock, flags); @@ -1491,8 +1501,7 @@ static int check_ro(struct sdhci_host *host) & SDHCI_WRITE_PROTECT); spin_unlock_irqrestore(&host->lock, flags); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, CLK_TIMEOUT); + sdhci_disable_clk(host, CLK_TIMEOUT); /* This quirk needs to be replaced by a callback-function later */ return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? @@ -2033,14 +2042,13 @@ static void sdhci_tasklet_finish(unsigned long param) host->cmd = NULL; host->data = NULL; -#ifndef SDHCI_USE_LEDS_CLASS +#ifndef CONFIG_SDHCI_USE_LEDS_CLASS sdhci_deactivate_led(host); #endif mmiowb(); spin_unlock_irqrestore(&host->lock, flags); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, CLK_TIMEOUT); + sdhci_disable_clk(host, CLK_TIMEOUT); mmc_request_done(host->mmc, mrq); } @@ -2386,8 +2394,7 @@ out: * mmc_suspend_host may disable the clk */ sdhci_enable_clk(host); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); return ret; } @@ -2409,12 +2416,13 @@ int sdhci_resume_host(struct sdhci_host *host) host->ops->enable_dma(host); } + sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); + ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, mmc_hostname(host->mmc), host); if (ret) goto out; - sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); mmiowb(); ret = mmc_resume_host(host->mmc); @@ -2425,8 +2433,7 @@ int sdhci_resume_host(struct sdhci_host *host) out: /* sync worker */ - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); /* Set the re-tuning expiration flag */ if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && @@ -2446,8 +2453,7 @@ void sdhci_enable_irq_wakeups(struct sdhci_host *host) val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); val |= SDHCI_WAKE_ON_INT; sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, CLK_TIMEOUT); + sdhci_disable_clk(host, CLK_TIMEOUT); } EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); @@ -2595,8 +2601,7 @@ int sdhci_add_host(struct sdhci_host *host) printk(KERN_ERR "%s: Hardware doesn't specify base clock " "frequency.\n", mmc_hostname(mmc)); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); return -ENODEV; } host->max_clk = host->ops->get_max_clock(host); @@ -2612,8 +2617,7 @@ int sdhci_add_host(struct sdhci_host *host) printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " "frequency.\n", mmc_hostname(mmc)); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); return -ENODEV; } } @@ -2653,7 +2657,7 @@ int sdhci_add_host(struct sdhci_host *host) } else mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; - mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; @@ -2664,6 +2668,7 @@ int sdhci_add_host(struct sdhci_host *host) ((host->flags & SDHCI_USE_ADMA) || !(host->flags & SDHCI_USE_SDMA))) { host->flags |= SDHCI_AUTO_CMD23; + mmc->caps |= MMC_CAP_CMD23; DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc)); } else { DBG("%s: Auto-CMD23 unavailable\n", mmc_hostname(mmc)); @@ -2802,8 +2807,7 @@ int sdhci_add_host(struct sdhci_host *host) if (mmc->ocr_avail == 0) { printk(KERN_ERR "%s: Hardware doesn't report any " "support voltages.\n", mmc_hostname(mmc)); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); return -ENODEV; } @@ -2904,7 +2908,7 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_dumpregs(host); #endif -#ifdef SDHCI_USE_LEDS_CLASS +#ifdef CONFIG_SDHCI_USE_LEDS_CLASS snprintf(host->led_name, sizeof(host->led_name), "%s::", mmc_hostname(mmc)); host->led.name = host->led_name; @@ -2927,11 +2931,10 @@ int sdhci_add_host(struct sdhci_host *host) (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); sdhci_enable_card_detection(host); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, CLK_TIMEOUT); + sdhci_disable_clk(host, CLK_TIMEOUT); return 0; -#ifdef SDHCI_USE_LEDS_CLASS +#ifdef CONFIG_SDHCI_USE_LEDS_CLASS reset: sdhci_reset(host, SDHCI_RESET_ALL); free_irq(host->irq, host); @@ -2939,8 +2942,7 @@ reset: untasklet: tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); return ret; } @@ -2973,15 +2975,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) mmc_remove_host(host->mmc); -#ifdef SDHCI_USE_LEDS_CLASS +#ifdef CONFIG_SDHCI_USE_LEDS_CLASS led_classdev_unregister(&host->led); #endif sdhci_enable_clk(host); if (!dead) sdhci_reset(host, SDHCI_RESET_ALL); - if (!sdhci_is_sdio_attached(host)) - sdhci_disable_clk(host, 0); + sdhci_disable_clk(host, 0); free_irq(host->irq, host); |