aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/at91_mci.c48
-rw-r--r--include/asm-arm/arch-at91/at91_mci.h4
2 files changed, 52 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 */
136static 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
133static void at91_timeout_timer(unsigned long data) 170static 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}
diff --git a/include/asm-arm/arch-at91/at91_mci.h b/include/asm-arm/arch-at91/at91_mci.h
index 1551fc24eb43..400ec10014b4 100644
--- a/include/asm-arm/arch-at91/at91_mci.h
+++ b/include/asm-arm/arch-at91/at91_mci.h
@@ -75,6 +75,10 @@
75#define AT91_MCI_TRTYP_MULTIPLE (1 << 19) 75#define AT91_MCI_TRTYP_MULTIPLE (1 << 19)
76#define AT91_MCI_TRTYP_STREAM (2 << 19) 76#define AT91_MCI_TRTYP_STREAM (2 << 19)
77 77
78#define AT91_MCI_BLKR 0x18 /* Block Register */
79#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */
80#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block lenght */
81
78#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */ 82#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */
79#define AT91_MCR_RDR 0x30 /* Receive Data Register */ 83#define AT91_MCR_RDR 0x30 /* Receive Data Register */
80#define AT91_MCR_TDR 0x34 /* Transmit Data Register */ 84#define AT91_MCR_TDR 0x34 /* Transmit Data Register */