aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/imxmmc.c54
1 files changed, 43 insertions, 11 deletions
diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c
index ffb7f55d3467..07f36c454bd6 100644
--- a/drivers/mmc/imxmmc.c
+++ b/drivers/mmc/imxmmc.c
@@ -102,6 +102,7 @@ struct imxmci_host {
102#define IMXMCI_PEND_CPU_DATA_b 5 102#define IMXMCI_PEND_CPU_DATA_b 5
103#define IMXMCI_PEND_CARD_XCHG_b 6 103#define IMXMCI_PEND_CARD_XCHG_b 6
104#define IMXMCI_PEND_SET_INIT_b 7 104#define IMXMCI_PEND_SET_INIT_b 7
105#define IMXMCI_PEND_STARTED_b 8
105 106
106#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b) 107#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b)
107#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b) 108#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b)
@@ -111,6 +112,7 @@ struct imxmci_host {
111#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b) 112#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b)
112#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b) 113#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b)
113#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b) 114#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b)
115#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b)
114 116
115static void imxmci_stop_clock(struct imxmci_host *host) 117static void imxmci_stop_clock(struct imxmci_host *host)
116{ 118{
@@ -131,23 +133,52 @@ static void imxmci_stop_clock(struct imxmci_host *host)
131 dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n"); 133 dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
132} 134}
133 135
134static void imxmci_start_clock(struct imxmci_host *host) 136static int imxmci_start_clock(struct imxmci_host *host)
135{ 137{
136 int i = 0; 138 unsigned int trials = 0;
139 unsigned int delay_limit = 128;
140 unsigned long flags;
141
137 MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK; 142 MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
138 while(i < 0x1000) {
139 if(!(i & 0x7f))
140 MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
141 143
142 if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) { 144 clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
143 /* Check twice before cut */ 145
146 /*
147 * Command start of the clock, this usually succeeds in less
148 * then 6 delay loops, but during card detection (low clockrate)
149 * it takes up to 5000 delay loops and sometimes fails for the first time
150 */
151 MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
152
153 do {
154 unsigned int delay = delay_limit;
155
156 while(delay--){
144 if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) 157 if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
145 return; 158 /* Check twice before cut */
159 if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
160 return 0;
161
162 if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
163 return 0;
146 } 164 }
147 165
148 i++; 166 local_irq_save(flags);
149 } 167 /*
150 dev_dbg(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); 168 * Ensure, that request is not doubled under all possible circumstances.
169 * It is possible, that cock running state is missed, because some other
170 * IRQ or schedule delays this function execution and the clocks has
171 * been already stopped by other means (response processing, SDHC HW)
172 */
173 if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
174 MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
175 local_irq_restore(flags);
176
177 } while(++trials<256);
178
179 dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
180
181 return -1;
151} 182}
152 183
153static void imxmci_softreset(void) 184static void imxmci_softreset(void)
@@ -622,6 +653,7 @@ static irqreturn_t imxmci_irq(int irq, void *devid, struct pt_regs *regs)
622 atomic_set(&host->stuck_timeout, 0); 653 atomic_set(&host->stuck_timeout, 0);
623 host->status_reg = stat; 654 host->status_reg = stat;
624 set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); 655 set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
656 set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
625 tasklet_schedule(&host->tasklet); 657 tasklet_schedule(&host->tasklet);
626 658
627 return IRQ_RETVAL(handled);; 659 return IRQ_RETVAL(handled);;