diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/at91_mci.c | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index fce171c7c67e..e9242110ce2e 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c | |||
@@ -130,6 +130,43 @@ struct at91mci_host | |||
130 | struct timer_list timer; | 130 | struct timer_list timer; |
131 | }; | 131 | }; |
132 | 132 | ||
133 | /* | ||
134 | * Reset the controller and restore most of the state | ||
135 | */ | ||
136 | static void at91_reset_host(struct at91mci_host *host) | ||
137 | { | ||
138 | unsigned long flags; | ||
139 | u32 mr; | ||
140 | u32 sdcr; | ||
141 | u32 dtor; | ||
142 | u32 imr; | ||
143 | |||
144 | local_irq_save(flags); | ||
145 | imr = at91_mci_read(host, AT91_MCI_IMR); | ||
146 | |||
147 | at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); | ||
148 | |||
149 | /* save current state */ | ||
150 | mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; | ||
151 | sdcr = at91_mci_read(host, AT91_MCI_SDCR); | ||
152 | dtor = at91_mci_read(host, AT91_MCI_DTOR); | ||
153 | |||
154 | /* reset the controller */ | ||
155 | at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); | ||
156 | |||
157 | /* restore state */ | ||
158 | at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); | ||
159 | at91_mci_write(host, AT91_MCI_MR, mr); | ||
160 | at91_mci_write(host, AT91_MCI_SDCR, sdcr); | ||
161 | at91_mci_write(host, AT91_MCI_DTOR, dtor); | ||
162 | at91_mci_write(host, AT91_MCI_IER, imr); | ||
163 | |||
164 | /* make sure sdio interrupts will fire */ | ||
165 | at91_mci_read(host, AT91_MCI_SR); | ||
166 | |||
167 | local_irq_restore(flags); | ||
168 | } | ||
169 | |||
133 | static void at91_timeout_timer(unsigned long data) | 170 | static void at91_timeout_timer(unsigned long data) |
134 | { | 171 | { |
135 | struct at91mci_host *host; | 172 | struct at91mci_host *host; |
@@ -148,6 +185,7 @@ static void at91_timeout_timer(unsigned long data) | |||
148 | host->request->cmd->error = -ETIMEDOUT; | 185 | host->request->cmd->error = -ETIMEDOUT; |
149 | } | 186 | } |
150 | 187 | ||
188 | at91_reset_host(host); | ||
151 | mmc_request_done(host->mmc, host->request); | 189 | mmc_request_done(host->mmc, host->request); |
152 | } | 190 | } |
153 | } | 191 | } |
@@ -512,6 +550,11 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command | |||
512 | mr |= AT91_MCI_PDCMODE; | 550 | mr |= AT91_MCI_PDCMODE; |
513 | at91_mci_write(host, AT91_MCI_MR, mr); | 551 | at91_mci_write(host, AT91_MCI_MR, mr); |
514 | 552 | ||
553 | if (!cpu_is_at91rm9200()) | ||
554 | at91_mci_write(host, AT91_MCI_BLKR, | ||
555 | AT91_MCI_BLKR_BCNT(blocks) | | ||
556 | AT91_MCI_BLKR_BLKLEN(block_length)); | ||
557 | |||
515 | /* | 558 | /* |
516 | * Disable the PDC controller | 559 | * Disable the PDC controller |
517 | */ | 560 | */ |
@@ -584,6 +627,11 @@ static void at91_mci_process_next(struct at91mci_host *host) | |||
584 | at91_mci_send_command(host, host->request->stop); | 627 | at91_mci_send_command(host, host->request->stop); |
585 | } else { | 628 | } else { |
586 | del_timer(&host->timer); | 629 | del_timer(&host->timer); |
630 | /* the at91rm9200 mci controller hangs after some transfers, | ||
631 | * and the workaround is to reset it after each transfer. | ||
632 | */ | ||
633 | if (cpu_is_at91rm9200()) | ||
634 | at91_reset_host(host); | ||
587 | mmc_request_done(host->mmc, host->request); | 635 | mmc_request_done(host->mmc, host->request); |
588 | } | 636 | } |
589 | } | 637 | } |