aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/core/core.c177
-rw-r--r--drivers/mmc/core/host.c1
-rw-r--r--drivers/mmc/core/host.h2
-rw-r--r--include/linux/mmc/host.h47
4 files changed, 221 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
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e9087e..338a9b3d51e4 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -51,6 +51,35 @@ struct mmc_ios {
51}; 51};
52 52
53struct mmc_host_ops { 53struct mmc_host_ops {
54 /*
55 * Hosts that support power saving can use the 'enable' and 'disable'
56 * methods to exit and enter power saving states. 'enable' is called
57 * when the host is claimed and 'disable' is called (or scheduled with
58 * a delay) when the host is released. The 'disable' is scheduled if
59 * the disable delay set by 'mmc_set_disable_delay()' is non-zero,
60 * otherwise 'disable' is called immediately. 'disable' may be
61 * scheduled repeatedly, to permit ever greater power saving at the
62 * expense of ever greater latency to re-enable. Rescheduling is
63 * determined by the return value of the 'disable' method. A positive
64 * value gives the delay in milliseconds.
65 *
66 * In the case where a host function (like set_ios) may be called
67 * with or without the host claimed, enabling and disabling can be
68 * done directly and will nest correctly. Call 'mmc_host_enable()' and
69 * 'mmc_host_lazy_disable()' for this purpose, but note that these
70 * functions must be paired.
71 *
72 * Alternatively, 'mmc_host_enable()' may be paired with
73 * 'mmc_host_disable()' which calls 'disable' immediately. In this
74 * case the 'disable' method will be called with 'lazy' set to 0.
75 * This is mainly useful for error paths.
76 *
77 * Because lazy disable may be called from a work queue, the 'disable'
78 * method must claim the host when 'lazy' != 0, which will work
79 * correctly because recursion is detected and handled.
80 */
81 int (*enable)(struct mmc_host *host);
82 int (*disable)(struct mmc_host *host, int lazy);
54 void (*request)(struct mmc_host *host, struct mmc_request *req); 83 void (*request)(struct mmc_host *host, struct mmc_request *req);
55 /* 84 /*
56 * Avoid calling these three functions too often or in a "fast path", 85 * Avoid calling these three functions too often or in a "fast path",
@@ -118,6 +147,7 @@ struct mmc_host {
118#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ 147#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
119#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ 148#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
120#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ 149#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
150#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
121 151
122 /* host specific block data */ 152 /* host specific block data */
123 unsigned int max_seg_size; /* see blk_queue_max_segment_size */ 153 unsigned int max_seg_size; /* see blk_queue_max_segment_size */
@@ -142,6 +172,13 @@ struct mmc_host {
142 unsigned int removed:1; /* host is being removed */ 172 unsigned int removed:1; /* host is being removed */
143#endif 173#endif
144 174
175 /* Only used with MMC_CAP_DISABLE */
176 int enabled; /* host is enabled */
177 int nesting_cnt; /* "enable" nesting count */
178 int en_dis_recurs; /* detect recursion */
179 unsigned int disable_delay; /* disable delay in msecs */
180 struct delayed_work disable; /* disabling work */
181
145 struct mmc_card *card; /* device attached to this host */ 182 struct mmc_card *card; /* device attached to this host */
146 183
147 wait_queue_head_t wq; 184 wait_queue_head_t wq;
@@ -197,5 +234,15 @@ struct regulator;
197int mmc_regulator_get_ocrmask(struct regulator *supply); 234int mmc_regulator_get_ocrmask(struct regulator *supply);
198int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); 235int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);
199 236
237int mmc_host_enable(struct mmc_host *host);
238int mmc_host_disable(struct mmc_host *host);
239int mmc_host_lazy_disable(struct mmc_host *host);
240
241static inline void mmc_set_disable_delay(struct mmc_host *host,
242 unsigned int disable_delay)
243{
244 host->disable_delay = disable_delay;
245}
246
200#endif 247#endif
201 248