aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/core.c177
-rw-r--r--drivers/mmc/core/host.c1
-rw-r--r--drivers/mmc/core/host.h2
3 files changed, 174 insertions, 6 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index e22d2b5576ec..fb24a096dba8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -344,6 +344,101 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
344EXPORT_SYMBOL(mmc_align_data_size); 344EXPORT_SYMBOL(mmc_align_data_size);
345 345
346/** 346/**
347 * mmc_host_enable - enable a host.
348 * @host: mmc host to enable
349 *
350 * Hosts that support power saving can use the 'enable' and 'disable'
351 * methods to exit and enter power saving states. For more information
352 * see comments for struct mmc_host_ops.
353 */
354int mmc_host_enable(struct mmc_host *host)
355{
356 if (!(host->caps & MMC_CAP_DISABLE))
357 return 0;
358
359 if (host->en_dis_recurs)
360 return 0;
361
362 if (host->nesting_cnt++)
363 return 0;
364
365 cancel_delayed_work_sync(&host->disable);
366
367 if (host->enabled)
368 return 0;
369
370 if (host->ops->enable) {
371 int err;
372
373 host->en_dis_recurs = 1;
374 err = host->ops->enable(host);
375 host->en_dis_recurs = 0;
376
377 if (err) {
378 pr_debug("%s: enable error %d\n",
379 mmc_hostname(host), err);
380 return err;
381 }
382 }
383 host->enabled = 1;
384 return 0;
385}
386EXPORT_SYMBOL(mmc_host_enable);
387
388static int mmc_host_do_disable(struct mmc_host *host, int lazy)
389{
390 if (host->ops->disable) {
391 int err;
392
393 host->en_dis_recurs = 1;
394 err = host->ops->disable(host, lazy);
395 host->en_dis_recurs = 0;
396
397 if (err < 0) {
398 pr_debug("%s: disable error %d\n",
399 mmc_hostname(host), err);
400 return err;
401 }
402 if (err > 0) {
403 unsigned long delay = msecs_to_jiffies(err);
404
405 mmc_schedule_delayed_work(&host->disable, delay);
406 }
407 }
408 host->enabled = 0;
409 return 0;
410}
411
412/**
413 * mmc_host_disable - disable a host.
414 * @host: mmc host to disable
415 *
416 * Hosts that support power saving can use the 'enable' and 'disable'
417 * methods to exit and enter power saving states. For more information
418 * see comments for struct mmc_host_ops.
419 */
420int mmc_host_disable(struct mmc_host *host)
421{
422 int err;
423
424 if (!(host->caps & MMC_CAP_DISABLE))
425 return 0;
426
427 if (host->en_dis_recurs)
428 return 0;
429
430 if (--host->nesting_cnt)
431 return 0;
432
433 if (!host->enabled)
434 return 0;
435
436 err = mmc_host_do_disable(host, 0);
437 return err;
438}
439EXPORT_SYMBOL(mmc_host_disable);
440
441/**
347 * __mmc_claim_host - exclusively claim a host 442 * __mmc_claim_host - exclusively claim a host
348 * @host: mmc host to claim 443 * @host: mmc host to claim
349 * @abort: whether or not the operation should be aborted 444 * @abort: whether or not the operation should be aborted
@@ -379,11 +474,81 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
379 wake_up(&host->wq); 474 wake_up(&host->wq);
380 spin_unlock_irqrestore(&host->lock, flags); 475 spin_unlock_irqrestore(&host->lock, flags);
381 remove_wait_queue(&host->wq, &wait); 476 remove_wait_queue(&host->wq, &wait);
477 if (!stop)
478 mmc_host_enable(host);
382 return stop; 479 return stop;
383} 480}
384 481
385EXPORT_SYMBOL(__mmc_claim_host); 482EXPORT_SYMBOL(__mmc_claim_host);
386 483
484static int mmc_try_claim_host(struct mmc_host *host)
485{
486 int claimed_host = 0;
487 unsigned long flags;
488
489 spin_lock_irqsave(&host->lock, flags);
490 if (!host->claimed) {
491 host->claimed = 1;
492 claimed_host = 1;
493 }
494 spin_unlock_irqrestore(&host->lock, flags);
495 return claimed_host;
496}
497
498static void mmc_do_release_host(struct mmc_host *host)
499{
500 unsigned long flags;
501
502 spin_lock_irqsave(&host->lock, flags);
503 host->claimed = 0;
504 spin_unlock_irqrestore(&host->lock, flags);
505
506 wake_up(&host->wq);
507}
508
509void mmc_host_deeper_disable(struct work_struct *work)
510{
511 struct mmc_host *host =
512 container_of(work, struct mmc_host, disable.work);
513
514 /* If the host is claimed then we do not want to disable it anymore */
515 if (!mmc_try_claim_host(host))
516 return;
517 mmc_host_do_disable(host, 1);
518 mmc_do_release_host(host);
519}
520
521/**
522 * mmc_host_lazy_disable - lazily disable a host.
523 * @host: mmc host to disable
524 *
525 * Hosts that support power saving can use the 'enable' and 'disable'
526 * methods to exit and enter power saving states. For more information
527 * see comments for struct mmc_host_ops.
528 */
529int mmc_host_lazy_disable(struct mmc_host *host)
530{
531 if (!(host->caps & MMC_CAP_DISABLE))
532 return 0;
533
534 if (host->en_dis_recurs)
535 return 0;
536
537 if (--host->nesting_cnt)
538 return 0;
539
540 if (!host->enabled)
541 return 0;
542
543 if (host->disable_delay) {
544 mmc_schedule_delayed_work(&host->disable,
545 msecs_to_jiffies(host->disable_delay));
546 return 0;
547 } else
548 return mmc_host_do_disable(host, 1);
549}
550EXPORT_SYMBOL(mmc_host_lazy_disable);
551
387/** 552/**
388 * mmc_release_host - release a host 553 * mmc_release_host - release a host
389 * @host: mmc host to release 554 * @host: mmc host to release
@@ -393,15 +558,11 @@ EXPORT_SYMBOL(__mmc_claim_host);
393 */ 558 */
394void mmc_release_host(struct mmc_host *host) 559void mmc_release_host(struct mmc_host *host)
395{ 560{
396 unsigned long flags;
397
398 WARN_ON(!host->claimed); 561 WARN_ON(!host->claimed);
399 562
400 spin_lock_irqsave(&host->lock, flags); 563 mmc_host_lazy_disable(host);
401 host->claimed = 0;
402 spin_unlock_irqrestore(&host->lock, flags);
403 564
404 wake_up(&host->wq); 565 mmc_do_release_host(host);
405} 566}
406 567
407EXPORT_SYMBOL(mmc_release_host); 568EXPORT_SYMBOL(mmc_release_host);
@@ -953,6 +1114,8 @@ void mmc_stop_host(struct mmc_host *host)
953 spin_unlock_irqrestore(&host->lock, flags); 1114 spin_unlock_irqrestore(&host->lock, flags);
954#endif 1115#endif
955 1116
1117 if (host->caps & MMC_CAP_DISABLE)
1118 cancel_delayed_work(&host->disable);
956 cancel_delayed_work(&host->detect); 1119 cancel_delayed_work(&host->detect);
957 mmc_flush_scheduled_work(); 1120 mmc_flush_scheduled_work();
958 1121
@@ -981,6 +1144,8 @@ void mmc_stop_host(struct mmc_host *host)
981 */ 1144 */
982int mmc_suspend_host(struct mmc_host *host, pm_message_t state) 1145int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
983{ 1146{
1147 if (host->caps & MMC_CAP_DISABLE)
1148 cancel_delayed_work(&host->disable);
984 cancel_delayed_work(&host->detect); 1149 cancel_delayed_work(&host->detect);
985 mmc_flush_scheduled_work(); 1150 mmc_flush_scheduled_work();
986 1151
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 5e945e64ead7..a268d12f1af0 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -83,6 +83,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
83 spin_lock_init(&host->lock); 83 spin_lock_init(&host->lock);
84 init_waitqueue_head(&host->wq); 84 init_waitqueue_head(&host->wq);
85 INIT_DELAYED_WORK(&host->detect, mmc_rescan); 85 INIT_DELAYED_WORK(&host->detect, mmc_rescan);
86 INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
86 87
87 /* 88 /*
88 * By default, hosts do not support SGIO or large requests. 89 * By default, hosts do not support SGIO or large requests.
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index c2dc3d2d9f9a..8c87e1109a34 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -14,5 +14,7 @@
14int mmc_register_host_class(void); 14int mmc_register_host_class(void);
15void mmc_unregister_host_class(void); 15void mmc_unregister_host_class(void);
16 16
17void mmc_host_deeper_disable(struct work_struct *work);
18
17#endif 19#endif
18 20