diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 177 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 1 | ||||
-rw-r--r-- | drivers/mmc/core/host.h | 2 |
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) | |||
344 | EXPORT_SYMBOL(mmc_align_data_size); | 344 | EXPORT_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 | */ | ||
354 | int 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 | } | ||
386 | EXPORT_SYMBOL(mmc_host_enable); | ||
387 | |||
388 | static 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 | */ | ||
420 | int 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 | } | ||
439 | EXPORT_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 | ||
385 | EXPORT_SYMBOL(__mmc_claim_host); | 482 | EXPORT_SYMBOL(__mmc_claim_host); |
386 | 483 | ||
484 | static 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 | |||
498 | static 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 | |||
509 | void 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 | */ | ||
529 | int 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 | } | ||
550 | EXPORT_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 | */ |
394 | void mmc_release_host(struct mmc_host *host) | 559 | void 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 | ||
407 | EXPORT_SYMBOL(mmc_release_host); | 568 | EXPORT_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 | */ |
982 | int mmc_suspend_host(struct mmc_host *host, pm_message_t state) | 1145 | int 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 @@ | |||
14 | int mmc_register_host_class(void); | 14 | int mmc_register_host_class(void); |
15 | void mmc_unregister_host_class(void); | 15 | void mmc_unregister_host_class(void); |
16 | 16 | ||
17 | void mmc_host_deeper_disable(struct work_struct *work); | ||
18 | |||
17 | #endif | 19 | #endif |
18 | 20 | ||