aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@nokia.com>2009-09-22 19:44:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:33 -0400
commit8ea926b22e2d13238e4d65d8f61c48fe424e6f4f (patch)
treed62846c0f7aa50afce52bd8a9ed4b2affeaa1889 /drivers/mmc
parent27cce39f555def6f5ebe7f03d69ccc44ab25f0b2 (diff)
mmc: add 'enable' and 'disable' methods to mmc host
MMC hosts that support power saving can use the 'enable' and 'disable' methods to exit and enter power saving states. An explanation of their use is provided in the comments added to include/linux/mmc/host.h. Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com> Acked-by: Matt Fleming <matt@console-pimps.org> Cc: Ian Molton <ian@mnementh.co.uk> Cc: "Roberto A. Foglietta" <roberto.foglietta@gmail.com> Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com> Cc: Denis Karpov <ext-denis.2.karpov@nokia.com> Cc: Pierre Ossman <pierre@ossman.eu> Cc: Philip Langdale <philipl@overt.org> Cc: "Madhusudhan" <madhu.cr@ti.com> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
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