|
@@ -102,6 +102,7 @@ struct imxmci_host {
|
|
|
#define IMXMCI_PEND_CPU_DATA_b 5
|
|
|
#define IMXMCI_PEND_CARD_XCHG_b 6
|
|
|
#define IMXMCI_PEND_SET_INIT_b 7
|
|
|
+#define IMXMCI_PEND_STARTED_b 8
|
|
|
|
|
|
#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b)
|
|
|
#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b)
|
|
@@ -111,6 +112,7 @@ struct imxmci_host {
|
|
|
#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b)
|
|
|
#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b)
|
|
|
#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b)
|
|
|
+#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b)
|
|
|
|
|
|
static void imxmci_stop_clock(struct imxmci_host *host)
|
|
|
{
|
|
@@ -131,23 +133,52 @@ static void imxmci_stop_clock(struct imxmci_host *host)
|
|
|
dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
|
|
|
}
|
|
|
|
|
|
-static void imxmci_start_clock(struct imxmci_host *host)
|
|
|
+static int imxmci_start_clock(struct imxmci_host *host)
|
|
|
{
|
|
|
- int i = 0;
|
|
|
+ unsigned int trials = 0;
|
|
|
+ unsigned int delay_limit = 128;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
|
|
|
- while(i < 0x1000) {
|
|
|
- if(!(i & 0x7f))
|
|
|
- MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
|
|
|
|
|
|
- if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) {
|
|
|
- /* Check twice before cut */
|
|
|
+ clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Command start of the clock, this usually succeeds in less
|
|
|
+ * then 6 delay loops, but during card detection (low clockrate)
|
|
|
+ * it takes up to 5000 delay loops and sometimes fails for the first time
|
|
|
+ */
|
|
|
+ MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
|
|
|
+
|
|
|
+ do {
|
|
|
+ unsigned int delay = delay_limit;
|
|
|
+
|
|
|
+ while(delay--){
|
|
|
if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
|
|
|
- return;
|
|
|
+ /* Check twice before cut */
|
|
|
+ if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- i++;
|
|
|
- }
|
|
|
- dev_dbg(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
|
|
|
+ local_irq_save(flags);
|
|
|
+ /*
|
|
|
+ * Ensure, that request is not doubled under all possible circumstances.
|
|
|
+ * It is possible, that cock running state is missed, because some other
|
|
|
+ * IRQ or schedule delays this function execution and the clocks has
|
|
|
+ * been already stopped by other means (response processing, SDHC HW)
|
|
|
+ */
|
|
|
+ if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
|
|
|
+ MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
|
|
|
+ local_irq_restore(flags);
|
|
|
+
|
|
|
+ } while(++trials<256);
|
|
|
+
|
|
|
+ dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
|
|
|
+
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
static void imxmci_softreset(void)
|
|
@@ -622,6 +653,7 @@ static irqreturn_t imxmci_irq(int irq, void *devid, struct pt_regs *regs)
|
|
|
atomic_set(&host->stuck_timeout, 0);
|
|
|
host->status_reg = stat;
|
|
|
set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
|
|
|
+ set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
|
|
|
return IRQ_RETVAL(handled);;
|