OSDN Git Service

Merge remote branch 'stable/linux-2.6.35.y' into android-2.6.35
[android-x86/kernel.git] / drivers / mmc / core / core.c
index 83e7543..56a8f94 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/scatterlist.h>
 #include <linux/log2.h>
 #include <linux/regulator/consumer.h>
+#include <linux/wakelock.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -38,6 +39,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%)
@@ -69,6 +71,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);
 }
 
@@ -545,9 +548,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);
 }
 
 /**
@@ -907,12 +913,7 @@ static void mmc_power_up(struct mmc_host *host)
         */
        mmc_delay(10);
 
-       if (host->f_min > 400000) {
-               pr_warning("%s: Minimum clock frequency too high for "
-                               "identification mode\n", mmc_hostname(host));
-               host->ios.clock = host->f_min;
-       } else
-               host->ios.clock = 400000;
+       host->ios.clock = host->f_min;
 
        host->ios.power_mode = MMC_POWER_ON;
        mmc_set_ios(host);
@@ -977,6 +978,30 @@ static inline void mmc_bus_put(struct mmc_host *host)
        spin_unlock_irqrestore(&host->lock, flags);
 }
 
+int mmc_resume_bus(struct mmc_host *host)
+{
+       if (!mmc_bus_needs_resume(host))
+               return -EINVAL;
+
+       printk("%s: Starting deferred resume\n", mmc_hostname(host));
+       host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+       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.
@@ -1058,6 +1083,7 @@ void mmc_rescan(struct work_struct *work)
        u32 ocr;
        int err;
        unsigned long flags;
+       int extend_wakelock = 0;
 
        spin_lock_irqsave(&host->lock, flags);
 
@@ -1075,6 +1101,12 @@ void mmc_rescan(struct work_struct *work)
        if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
                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;
+
        mmc_bus_put(host);
 
 
@@ -1112,6 +1144,7 @@ void mmc_rescan(struct work_struct *work)
        if (!err) {
                if (mmc_attach_sdio(host, ocr))
                        mmc_power_off(host);
+               extend_wakelock = 1;
                goto out;
        }
 
@@ -1122,6 +1155,7 @@ void mmc_rescan(struct work_struct *work)
        if (!err) {
                if (mmc_attach_sd(host, ocr))
                        mmc_power_off(host);
+               extend_wakelock = 1;
                goto out;
        }
 
@@ -1132,6 +1166,7 @@ void mmc_rescan(struct work_struct *work)
        if (!err) {
                if (mmc_attach_mmc(host, ocr))
                        mmc_power_off(host);
+               extend_wakelock = 1;
                goto out;
        }
 
@@ -1139,6 +1174,11 @@ void mmc_rescan(struct work_struct *work)
        mmc_power_off(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);
 }
@@ -1268,6 +1308,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);
@@ -1297,6 +1340,12 @@ int mmc_resume_host(struct mmc_host *host)
        int err = 0;
 
        mmc_bus_get(host);
+       if (host->bus_resume_flags & MMC_BUSRESUME_MANUAL_RESUME) {
+               host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+               mmc_bus_put(host);
+               return 0;
+       }
+
        if (host->bus_ops && !host->bus_dead) {
                if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
                        mmc_power_up(host);
@@ -1365,10 +1414,28 @@ 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;
 
+       wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, "mmc_delayed_work");
+
        workqueue = create_singlethread_workqueue("kmmcd");
        if (!workqueue)
                return -ENOMEM;
@@ -1403,6 +1470,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);