diff options
Diffstat (limited to 'drivers/mmc/core/core.c')
-rw-r--r-- | drivers/mmc/core/core.c | 252 |
1 files changed, 57 insertions, 195 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 132378b89d76..14f262e9246d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) | |||
188 | struct scatterlist *sg; | 188 | struct scatterlist *sg; |
189 | #endif | 189 | #endif |
190 | 190 | ||
191 | if (mrq->sbc) { | ||
192 | pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", | ||
193 | mmc_hostname(host), mrq->sbc->opcode, | ||
194 | mrq->sbc->arg, mrq->sbc->flags); | ||
195 | } | ||
196 | |||
191 | pr_debug("%s: starting CMD%u arg %08x flags %08x\n", | 197 | pr_debug("%s: starting CMD%u arg %08x flags %08x\n", |
192 | mmc_hostname(host), mrq->cmd->opcode, | 198 | mmc_hostname(host), mrq->cmd->opcode, |
193 | mrq->cmd->arg, mrq->cmd->flags); | 199 | mrq->cmd->arg, mrq->cmd->flags); |
@@ -243,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq) | |||
243 | complete(&mrq->completion); | 249 | complete(&mrq->completion); |
244 | } | 250 | } |
245 | 251 | ||
246 | static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) | 252 | static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) |
247 | { | 253 | { |
248 | init_completion(&mrq->completion); | 254 | init_completion(&mrq->completion); |
249 | mrq->done = mmc_wait_done; | 255 | mrq->done = mmc_wait_done; |
250 | if (mmc_card_removed(host->card)) { | 256 | if (mmc_card_removed(host->card)) { |
251 | mrq->cmd->error = -ENOMEDIUM; | 257 | mrq->cmd->error = -ENOMEDIUM; |
252 | complete(&mrq->completion); | 258 | complete(&mrq->completion); |
253 | return; | 259 | return -ENOMEDIUM; |
254 | } | 260 | } |
255 | mmc_start_request(host, mrq); | 261 | mmc_start_request(host, mrq); |
262 | return 0; | ||
256 | } | 263 | } |
257 | 264 | ||
258 | static void mmc_wait_for_req_done(struct mmc_host *host, | 265 | static void mmc_wait_for_req_done(struct mmc_host *host, |
@@ -336,6 +343,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, | |||
336 | struct mmc_async_req *areq, int *error) | 343 | struct mmc_async_req *areq, int *error) |
337 | { | 344 | { |
338 | int err = 0; | 345 | int err = 0; |
346 | int start_err = 0; | ||
339 | struct mmc_async_req *data = host->areq; | 347 | struct mmc_async_req *data = host->areq; |
340 | 348 | ||
341 | /* Prepare a new request */ | 349 | /* Prepare a new request */ |
@@ -345,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, | |||
345 | if (host->areq) { | 353 | if (host->areq) { |
346 | mmc_wait_for_req_done(host, host->areq->mrq); | 354 | mmc_wait_for_req_done(host, host->areq->mrq); |
347 | err = host->areq->err_check(host->card, host->areq); | 355 | err = host->areq->err_check(host->card, host->areq); |
348 | if (err) { | ||
349 | /* post process the completed failed request */ | ||
350 | mmc_post_req(host, host->areq->mrq, 0); | ||
351 | if (areq) | ||
352 | /* | ||
353 | * Cancel the new prepared request, because | ||
354 | * it can't run until the failed | ||
355 | * request has been properly handled. | ||
356 | */ | ||
357 | mmc_post_req(host, areq->mrq, -EINVAL); | ||
358 | |||
359 | host->areq = NULL; | ||
360 | goto out; | ||
361 | } | ||
362 | } | 356 | } |
363 | 357 | ||
364 | if (areq) | 358 | if (!err && areq) |
365 | __mmc_start_req(host, areq->mrq); | 359 | start_err = __mmc_start_req(host, areq->mrq); |
366 | 360 | ||
367 | if (host->areq) | 361 | if (host->areq) |
368 | mmc_post_req(host, host->areq->mrq, 0); | 362 | mmc_post_req(host, host->areq->mrq, 0); |
369 | 363 | ||
370 | host->areq = areq; | 364 | /* Cancel a prepared request if it was not started. */ |
371 | out: | 365 | if ((err || start_err) && areq) |
366 | mmc_post_req(host, areq->mrq, -EINVAL); | ||
367 | |||
368 | if (err) | ||
369 | host->areq = NULL; | ||
370 | else | ||
371 | host->areq = areq; | ||
372 | |||
372 | if (error) | 373 | if (error) |
373 | *error = err; | 374 | *error = err; |
374 | return data; | 375 | return data; |
@@ -599,105 +600,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) | |||
599 | EXPORT_SYMBOL(mmc_align_data_size); | 600 | EXPORT_SYMBOL(mmc_align_data_size); |
600 | 601 | ||
601 | /** | 602 | /** |
602 | * mmc_host_enable - enable a host. | ||
603 | * @host: mmc host to enable | ||
604 | * | ||
605 | * Hosts that support power saving can use the 'enable' and 'disable' | ||
606 | * methods to exit and enter power saving states. For more information | ||
607 | * see comments for struct mmc_host_ops. | ||
608 | */ | ||
609 | int mmc_host_enable(struct mmc_host *host) | ||
610 | { | ||
611 | if (!(host->caps & MMC_CAP_DISABLE)) | ||
612 | return 0; | ||
613 | |||
614 | if (host->en_dis_recurs) | ||
615 | return 0; | ||
616 | |||
617 | if (host->nesting_cnt++) | ||
618 | return 0; | ||
619 | |||
620 | cancel_delayed_work_sync(&host->disable); | ||
621 | |||
622 | if (host->enabled) | ||
623 | return 0; | ||
624 | |||
625 | if (host->ops->enable) { | ||
626 | int err; | ||
627 | |||
628 | host->en_dis_recurs = 1; | ||
629 | mmc_host_clk_hold(host); | ||
630 | err = host->ops->enable(host); | ||
631 | mmc_host_clk_release(host); | ||
632 | host->en_dis_recurs = 0; | ||
633 | |||
634 | if (err) { | ||
635 | pr_debug("%s: enable error %d\n", | ||
636 | mmc_hostname(host), err); | ||
637 | return err; | ||
638 | } | ||
639 | } | ||
640 | host->enabled = 1; | ||
641 | return 0; | ||
642 | } | ||
643 | EXPORT_SYMBOL(mmc_host_enable); | ||
644 | |||
645 | static int mmc_host_do_disable(struct mmc_host *host, int lazy) | ||
646 | { | ||
647 | if (host->ops->disable) { | ||
648 | int err; | ||
649 | |||
650 | host->en_dis_recurs = 1; | ||
651 | mmc_host_clk_hold(host); | ||
652 | err = host->ops->disable(host, lazy); | ||
653 | mmc_host_clk_release(host); | ||
654 | host->en_dis_recurs = 0; | ||
655 | |||
656 | if (err < 0) { | ||
657 | pr_debug("%s: disable error %d\n", | ||
658 | mmc_hostname(host), err); | ||
659 | return err; | ||
660 | } | ||
661 | if (err > 0) { | ||
662 | unsigned long delay = msecs_to_jiffies(err); | ||
663 | |||
664 | mmc_schedule_delayed_work(&host->disable, delay); | ||
665 | } | ||
666 | } | ||
667 | host->enabled = 0; | ||
668 | return 0; | ||
669 | } | ||
670 | |||
671 | /** | ||
672 | * mmc_host_disable - disable a host. | ||
673 | * @host: mmc host to disable | ||
674 | * | ||
675 | * Hosts that support power saving can use the 'enable' and 'disable' | ||
676 | * methods to exit and enter power saving states. For more information | ||
677 | * see comments for struct mmc_host_ops. | ||
678 | */ | ||
679 | int mmc_host_disable(struct mmc_host *host) | ||
680 | { | ||
681 | int err; | ||
682 | |||
683 | if (!(host->caps & MMC_CAP_DISABLE)) | ||
684 | return 0; | ||
685 | |||
686 | if (host->en_dis_recurs) | ||
687 | return 0; | ||
688 | |||
689 | if (--host->nesting_cnt) | ||
690 | return 0; | ||
691 | |||
692 | if (!host->enabled) | ||
693 | return 0; | ||
694 | |||
695 | err = mmc_host_do_disable(host, 0); | ||
696 | return err; | ||
697 | } | ||
698 | EXPORT_SYMBOL(mmc_host_disable); | ||
699 | |||
700 | /** | ||
701 | * __mmc_claim_host - exclusively claim a host | 603 | * __mmc_claim_host - exclusively claim a host |
702 | * @host: mmc host to claim | 604 | * @host: mmc host to claim |
703 | * @abort: whether or not the operation should be aborted | 605 | * @abort: whether or not the operation should be aborted |
@@ -735,8 +637,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) | |||
735 | wake_up(&host->wq); | 637 | wake_up(&host->wq); |
736 | spin_unlock_irqrestore(&host->lock, flags); | 638 | spin_unlock_irqrestore(&host->lock, flags); |
737 | remove_wait_queue(&host->wq, &wait); | 639 | remove_wait_queue(&host->wq, &wait); |
738 | if (!stop) | 640 | if (host->ops->enable && !stop && host->claim_cnt == 1) |
739 | mmc_host_enable(host); | 641 | host->ops->enable(host); |
740 | return stop; | 642 | return stop; |
741 | } | 643 | } |
742 | 644 | ||
@@ -761,21 +663,28 @@ int mmc_try_claim_host(struct mmc_host *host) | |||
761 | claimed_host = 1; | 663 | claimed_host = 1; |
762 | } | 664 | } |
763 | spin_unlock_irqrestore(&host->lock, flags); | 665 | spin_unlock_irqrestore(&host->lock, flags); |
666 | if (host->ops->enable && claimed_host && host->claim_cnt == 1) | ||
667 | host->ops->enable(host); | ||
764 | return claimed_host; | 668 | return claimed_host; |
765 | } | 669 | } |
766 | EXPORT_SYMBOL(mmc_try_claim_host); | 670 | EXPORT_SYMBOL(mmc_try_claim_host); |
767 | 671 | ||
768 | /** | 672 | /** |
769 | * mmc_do_release_host - release a claimed host | 673 | * mmc_release_host - release a host |
770 | * @host: mmc host to release | 674 | * @host: mmc host to release |
771 | * | 675 | * |
772 | * If you successfully claimed a host, this function will | 676 | * Release a MMC host, allowing others to claim the host |
773 | * release it again. | 677 | * for their operations. |
774 | */ | 678 | */ |
775 | void mmc_do_release_host(struct mmc_host *host) | 679 | void mmc_release_host(struct mmc_host *host) |
776 | { | 680 | { |
777 | unsigned long flags; | 681 | unsigned long flags; |
778 | 682 | ||
683 | WARN_ON(!host->claimed); | ||
684 | |||
685 | if (host->ops->disable && host->claim_cnt == 1) | ||
686 | host->ops->disable(host); | ||
687 | |||
779 | spin_lock_irqsave(&host->lock, flags); | 688 | spin_lock_irqsave(&host->lock, flags); |
780 | if (--host->claim_cnt) { | 689 | if (--host->claim_cnt) { |
781 | /* Release for nested claim */ | 690 | /* Release for nested claim */ |
@@ -787,67 +696,6 @@ void mmc_do_release_host(struct mmc_host *host) | |||
787 | wake_up(&host->wq); | 696 | wake_up(&host->wq); |
788 | } | 697 | } |
789 | } | 698 | } |
790 | EXPORT_SYMBOL(mmc_do_release_host); | ||
791 | |||
792 | void mmc_host_deeper_disable(struct work_struct *work) | ||
793 | { | ||
794 | struct mmc_host *host = | ||
795 | container_of(work, struct mmc_host, disable.work); | ||
796 | |||
797 | /* If the host is claimed then we do not want to disable it anymore */ | ||
798 | if (!mmc_try_claim_host(host)) | ||
799 | return; | ||
800 | mmc_host_do_disable(host, 1); | ||
801 | mmc_do_release_host(host); | ||
802 | } | ||
803 | |||
804 | /** | ||
805 | * mmc_host_lazy_disable - lazily disable a host. | ||
806 | * @host: mmc host to disable | ||
807 | * | ||
808 | * Hosts that support power saving can use the 'enable' and 'disable' | ||
809 | * methods to exit and enter power saving states. For more information | ||
810 | * see comments for struct mmc_host_ops. | ||
811 | */ | ||
812 | int mmc_host_lazy_disable(struct mmc_host *host) | ||
813 | { | ||
814 | if (!(host->caps & MMC_CAP_DISABLE)) | ||
815 | return 0; | ||
816 | |||
817 | if (host->en_dis_recurs) | ||
818 | return 0; | ||
819 | |||
820 | if (--host->nesting_cnt) | ||
821 | return 0; | ||
822 | |||
823 | if (!host->enabled) | ||
824 | return 0; | ||
825 | |||
826 | if (host->disable_delay) { | ||
827 | mmc_schedule_delayed_work(&host->disable, | ||
828 | msecs_to_jiffies(host->disable_delay)); | ||
829 | return 0; | ||
830 | } else | ||
831 | return mmc_host_do_disable(host, 1); | ||
832 | } | ||
833 | EXPORT_SYMBOL(mmc_host_lazy_disable); | ||
834 | |||
835 | /** | ||
836 | * mmc_release_host - release a host | ||
837 | * @host: mmc host to release | ||
838 | * | ||
839 | * Release a MMC host, allowing others to claim the host | ||
840 | * for their operations. | ||
841 | */ | ||
842 | void mmc_release_host(struct mmc_host *host) | ||
843 | { | ||
844 | WARN_ON(!host->claimed); | ||
845 | |||
846 | mmc_host_lazy_disable(host); | ||
847 | |||
848 | mmc_do_release_host(host); | ||
849 | } | ||
850 | |||
851 | EXPORT_SYMBOL(mmc_release_host); | 699 | EXPORT_SYMBOL(mmc_release_host); |
852 | 700 | ||
853 | /* | 701 | /* |
@@ -2115,18 +1963,36 @@ int _mmc_detect_card_removed(struct mmc_host *host) | |||
2115 | int mmc_detect_card_removed(struct mmc_host *host) | 1963 | int mmc_detect_card_removed(struct mmc_host *host) |
2116 | { | 1964 | { |
2117 | struct mmc_card *card = host->card; | 1965 | struct mmc_card *card = host->card; |
1966 | int ret; | ||
2118 | 1967 | ||
2119 | WARN_ON(!host->claimed); | 1968 | WARN_ON(!host->claimed); |
1969 | |||
1970 | if (!card) | ||
1971 | return 1; | ||
1972 | |||
1973 | ret = mmc_card_removed(card); | ||
2120 | /* | 1974 | /* |
2121 | * The card will be considered unchanged unless we have been asked to | 1975 | * The card will be considered unchanged unless we have been asked to |
2122 | * detect a change or host requires polling to provide card detection. | 1976 | * detect a change or host requires polling to provide card detection. |
2123 | */ | 1977 | */ |
2124 | if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL)) | 1978 | if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) && |
2125 | return mmc_card_removed(card); | 1979 | !(host->caps2 & MMC_CAP2_DETECT_ON_ERR)) |
1980 | return ret; | ||
2126 | 1981 | ||
2127 | host->detect_change = 0; | 1982 | host->detect_change = 0; |
1983 | if (!ret) { | ||
1984 | ret = _mmc_detect_card_removed(host); | ||
1985 | if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) { | ||
1986 | /* | ||
1987 | * Schedule a detect work as soon as possible to let a | ||
1988 | * rescan handle the card removal. | ||
1989 | */ | ||
1990 | cancel_delayed_work(&host->detect); | ||
1991 | mmc_detect_change(host, 0); | ||
1992 | } | ||
1993 | } | ||
2128 | 1994 | ||
2129 | return _mmc_detect_card_removed(host); | 1995 | return ret; |
2130 | } | 1996 | } |
2131 | EXPORT_SYMBOL(mmc_detect_card_removed); | 1997 | EXPORT_SYMBOL(mmc_detect_card_removed); |
2132 | 1998 | ||
@@ -2203,8 +2069,6 @@ void mmc_stop_host(struct mmc_host *host) | |||
2203 | spin_unlock_irqrestore(&host->lock, flags); | 2069 | spin_unlock_irqrestore(&host->lock, flags); |
2204 | #endif | 2070 | #endif |
2205 | 2071 | ||
2206 | if (host->caps & MMC_CAP_DISABLE) | ||
2207 | cancel_delayed_work(&host->disable); | ||
2208 | cancel_delayed_work_sync(&host->detect); | 2072 | cancel_delayed_work_sync(&host->detect); |
2209 | mmc_flush_scheduled_work(); | 2073 | mmc_flush_scheduled_work(); |
2210 | 2074 | ||
@@ -2399,13 +2263,11 @@ int mmc_suspend_host(struct mmc_host *host) | |||
2399 | { | 2263 | { |
2400 | int err = 0; | 2264 | int err = 0; |
2401 | 2265 | ||
2402 | if (host->caps & MMC_CAP_DISABLE) | ||
2403 | cancel_delayed_work(&host->disable); | ||
2404 | cancel_delayed_work(&host->detect); | 2266 | cancel_delayed_work(&host->detect); |
2405 | mmc_flush_scheduled_work(); | 2267 | mmc_flush_scheduled_work(); |
2406 | if (mmc_try_claim_host(host)) { | 2268 | if (mmc_try_claim_host(host)) { |
2407 | err = mmc_cache_ctrl(host, 0); | 2269 | err = mmc_cache_ctrl(host, 0); |
2408 | mmc_do_release_host(host); | 2270 | mmc_release_host(host); |
2409 | } else { | 2271 | } else { |
2410 | err = -EBUSY; | 2272 | err = -EBUSY; |
2411 | } | 2273 | } |
@@ -2426,7 +2288,7 @@ int mmc_suspend_host(struct mmc_host *host) | |||
2426 | if (host->bus_ops->suspend) { | 2288 | if (host->bus_ops->suspend) { |
2427 | err = host->bus_ops->suspend(host); | 2289 | err = host->bus_ops->suspend(host); |
2428 | } | 2290 | } |
2429 | mmc_do_release_host(host); | 2291 | mmc_release_host(host); |
2430 | 2292 | ||
2431 | if (err == -ENOSYS || !host->bus_ops->resume) { | 2293 | if (err == -ENOSYS || !host->bus_ops->resume) { |
2432 | /* | 2294 | /* |