diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/card/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mmc/card/block.c | 27 | ||||
-rw-r--r-- | drivers/mmc/core/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 91 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 7 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 86 | ||||
-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 | 33 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 2 |
10 files changed, 403 insertions, 30 deletions
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 3b1f783..ebb4afe 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 f85e422..c779503 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -126,11 +126,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) static inline int mmc_get_devidx(struct gendisk *disk) { - int devmaj = MAJOR(disk_devt(disk)); - int devidx = MINOR(disk_devt(disk)) / perdev_minors; - - if (!devmaj) - devidx = disk->first_minor / perdev_minors; + int devidx = disk->first_minor / perdev_minors; return devidx; } @@ -818,6 +814,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) continue; } status = get_card_status(card, req); + } else if (disable_multi == 1) { + disable_multi = 0; } if (brq.sbc.error) { @@ -939,12 +937,22 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) 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 + mmc_claim_host(card->host); ret = mmc_blk_part_switch(card, md); if (ret) { @@ -1038,6 +1046,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, md->disk->queue = md->queue.queue; md->disk->driverfs_dev = parent; set_disk_ro(md->disk, md->read_only || default_ro); + md->disk->flags = GENHD_FL_EXT_DEVT; /* * As discussed on lkml, GENHD_FL_REMOVABLE should: @@ -1277,6 +1286,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; @@ -1302,6 +1314,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 @@ -1325,7 +1340,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 ef10387..85c2e1a 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 7843efe..7c3444a 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> @@ -39,6 +40,7 @@ #include "sdio_ops.h" static struct workqueue_struct *workqueue; +static struct wake_lock mmc_delayed_work_wake_lock; /* * Enabling software CRCs on the data blocks can be a significant (30%) @@ -71,6 +73,7 @@ MODULE_PARM_DESC( static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) { + wake_lock(&mmc_delayed_work_wake_lock); return queue_delayed_work(workqueue, work, delay); } @@ -556,9 +559,12 @@ void mmc_host_deeper_disable(struct work_struct *work) /* If the host is claimed then we do not want to disable it anymore */ if (!mmc_try_claim_host(host)) - return; + goto out; mmc_host_do_disable(host, 1); mmc_do_release_host(host); + +out: + wake_unlock(&mmc_delayed_work_wake_lock); } /** @@ -1094,6 +1100,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. @@ -1568,6 +1604,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; @@ -1582,6 +1619,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. @@ -1606,14 +1649,20 @@ 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 (extend_wakelock) + wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2); + else + wake_unlock(&mmc_delayed_work_wake_lock); if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); } @@ -1751,6 +1800,9 @@ 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); @@ -1793,6 +1845,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); @@ -1843,6 +1901,10 @@ 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); @@ -1865,6 +1927,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); @@ -1875,6 +1941,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; @@ -1883,6 +1965,9 @@ static int __init mmc_init(void) if (!workqueue) return -ENOMEM; + wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, + "mmc_delayed_work"); + ret = mmc_register_bus(); if (ret) goto destroy_workqueue; @@ -1903,6 +1988,7 @@ unregister_bus: mmc_unregister_bus(); destroy_workqueue: destroy_workqueue(workqueue); + wake_lock_destroy(&mmc_delayed_work_wake_lock); return ret; } @@ -1913,6 +1999,7 @@ static void __exit mmc_exit(void) mmc_unregister_host_class(); mmc_unregister_bus(); destroy_workqueue(workqueue); + wake_lock_destroy(&mmc_delayed_work_wake_lock); } subsys_initcall(mmc_init); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b29d3e8..84694a9 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -336,7 +336,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 +354,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 diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index ff27741..5decf49 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -764,6 +764,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) { /* @@ -790,7 +793,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; } @@ -989,18 +1011,36 @@ 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); - + mmc_claim_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) { @@ -1038,12 +1078,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; @@ -1095,6 +1154,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); @@ -1159,9 +1221,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 262fff0..7da522e 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 && @@ -826,14 +846,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) */ @@ -881,3 +923,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 d2565df..52429a9 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 0f687cd..549a341 100644..100755 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -383,6 +383,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) EXPORT_SYMBOL_GPL(sdio_readb); /** + * sdio_readb_ext - read a single byte from a SDIO function + * @func: SDIO function to access + * @addr: address to read + * @err_ret: optional status value from transfer + * @in: value to add to argument + * + * Reads a single byte from the address space of a given SDIO + * function. If there is a problem reading the address, 0xff + * is returned and @err_ret will contain the error code. + */ +unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, + int *err_ret, unsigned in) +{ + int ret; + unsigned char val; + + BUG_ON(!func); + + if (err_ret) + *err_ret = 0; + + ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); + if (ret) { + if (err_ret) + *err_ret = ret; + return 0xFF; + } + + return val; +} +EXPORT_SYMBOL_GPL(sdio_readb_ext); + +/** * sdio_writeb - write a single byte to a SDIO function * @func: SDIO function to access * @b: byte to write diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 58d5436..7fc5848 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1044,7 +1044,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) { |