]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/mmc/core/core.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[mv-sheeva.git] / drivers / mmc / core / core.c
index fb24a096dba8de20d8b6ccf7078200295b65312e..7dab2e5f4bc9c87c53779a3d49cf2cb368114ef7 100644 (file)
@@ -461,16 +461,18 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
        while (1) {
                set_current_state(TASK_UNINTERRUPTIBLE);
                stop = abort ? atomic_read(abort) : 0;
-               if (stop || !host->claimed)
+               if (stop || !host->claimed || host->claimer == current)
                        break;
                spin_unlock_irqrestore(&host->lock, flags);
                schedule();
                spin_lock_irqsave(&host->lock, flags);
        }
        set_current_state(TASK_RUNNING);
-       if (!stop)
+       if (!stop) {
                host->claimed = 1;
-       else
+               host->claimer = current;
+               host->claim_cnt += 1;
+       } else
                wake_up(&host->wq);
        spin_unlock_irqrestore(&host->lock, flags);
        remove_wait_queue(&host->wq, &wait);
@@ -481,29 +483,43 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
 
 EXPORT_SYMBOL(__mmc_claim_host);
 
-static int mmc_try_claim_host(struct mmc_host *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) {
+       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);
        return claimed_host;
 }
+EXPORT_SYMBOL(mmc_try_claim_host);
 
 static void mmc_do_release_host(struct mmc_host *host)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&host->lock, flags);
-       host->claimed = 0;
-       spin_unlock_irqrestore(&host->lock, flags);
-
-       wake_up(&host->wq);
+       if (--host->claim_cnt) {
+               /* Release for nested claim */
+               spin_unlock_irqrestore(&host->lock, flags);
+       } else {
+               host->claimed = 0;
+               host->claimer = NULL;
+               spin_unlock_irqrestore(&host->lock, flags);
+               wake_up(&host->wq);
+       }
 }
 
 void mmc_host_deeper_disable(struct work_struct *work)
@@ -1127,6 +1143,8 @@ void mmc_stop_host(struct mmc_host *host)
                mmc_claim_host(host);
                mmc_detach_bus(host);
                mmc_release_host(host);
+               mmc_bus_put(host);
+               return;
        }
        mmc_bus_put(host);
 
@@ -1135,6 +1153,80 @@ void mmc_stop_host(struct mmc_host *host)
        mmc_power_off(host);
 }
 
+void mmc_power_save_host(struct mmc_host *host)
+{
+       mmc_bus_get(host);
+
+       if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+               mmc_bus_put(host);
+               return;
+       }
+
+       if (host->bus_ops->power_save)
+               host->bus_ops->power_save(host);
+
+       mmc_bus_put(host);
+
+       mmc_power_off(host);
+}
+EXPORT_SYMBOL(mmc_power_save_host);
+
+void mmc_power_restore_host(struct mmc_host *host)
+{
+       mmc_bus_get(host);
+
+       if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+               mmc_bus_put(host);
+               return;
+       }
+
+       mmc_power_up(host);
+       host->bus_ops->power_restore(host);
+
+       mmc_bus_put(host);
+}
+EXPORT_SYMBOL(mmc_power_restore_host);
+
+int mmc_card_awake(struct mmc_host *host)
+{
+       int err = -ENOSYS;
+
+       mmc_bus_get(host);
+
+       if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
+               err = host->bus_ops->awake(host);
+
+       mmc_bus_put(host);
+
+       return err;
+}
+EXPORT_SYMBOL(mmc_card_awake);
+
+int mmc_card_sleep(struct mmc_host *host)
+{
+       int err = -ENOSYS;
+
+       mmc_bus_get(host);
+
+       if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
+               err = host->bus_ops->sleep(host);
+
+       mmc_bus_put(host);
+
+       return err;
+}
+EXPORT_SYMBOL(mmc_card_sleep);
+
+int mmc_card_can_sleep(struct mmc_host *host)
+{
+       struct mmc_card *card = host->card;
+
+       if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
+               return 1;
+       return 0;
+}
+EXPORT_SYMBOL(mmc_card_can_sleep);
+
 #ifdef CONFIG_PM
 
 /**
@@ -1144,6 +1236,8 @@ void mmc_stop_host(struct mmc_host *host)
  */
 int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
+       int err = 0;
+
        if (host->caps & MMC_CAP_DISABLE)
                cancel_delayed_work(&host->disable);
        cancel_delayed_work(&host->detect);
@@ -1152,21 +1246,26 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
                if (host->bus_ops->suspend)
-                       host->bus_ops->suspend(host);
-               if (!host->bus_ops->resume) {
+                       err = host->bus_ops->suspend(host);
+               if (err == -ENOSYS || !host->bus_ops->resume) {
+                       /*
+                        * We simply "remove" the card in this case.
+                        * It will be redetected on resume.
+                        */
                        if (host->bus_ops->remove)
                                host->bus_ops->remove(host);
-
                        mmc_claim_host(host);
                        mmc_detach_bus(host);
                        mmc_release_host(host);
+                       err = 0;
                }
        }
        mmc_bus_put(host);
 
-       mmc_power_off(host);
+       if (!err)
+               mmc_power_off(host);
 
-       return 0;
+       return err;
 }
 
 EXPORT_SYMBOL(mmc_suspend_host);
@@ -1177,12 +1276,26 @@ EXPORT_SYMBOL(mmc_suspend_host);
  */
 int mmc_resume_host(struct mmc_host *host)
 {
+       int err = 0;
+
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
                mmc_power_up(host);
                mmc_select_voltage(host, host->ocr);
                BUG_ON(!host->bus_ops->resume);
-               host->bus_ops->resume(host);
+               err = host->bus_ops->resume(host);
+               if (err) {
+                       printk(KERN_WARNING "%s: error %d during resume "
+                                           "(card was removed?)\n",
+                                           mmc_hostname(host), err);
+                       if (host->bus_ops->remove)
+                               host->bus_ops->remove(host);
+                       mmc_claim_host(host);
+                       mmc_detach_bus(host);
+                       mmc_release_host(host);
+                       /* no need to bother upper layers */
+                       err = 0;
+               }
        }
        mmc_bus_put(host);
 
@@ -1192,7 +1305,7 @@ int mmc_resume_host(struct mmc_host *host)
         */
        mmc_detect_change(host, 1);
 
-       return 0;
+       return err;
 }
 
 EXPORT_SYMBOL(mmc_resume_host);