summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/imxmmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/imxmmc.c')
-rw-r--r--drivers/mmc/imxmmc.c84
1 files changed, 58 insertions, 26 deletions
diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c
index ffb7f55d3467..a4eb1d0e7a71 100644
--- a/drivers/mmc/imxmmc.c
+++ b/drivers/mmc/imxmmc.c
@@ -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)
@@ -187,8 +218,10 @@ static int imxmci_busy_wait_for_status(struct imxmci_host *host,
if(!loops)
return 0;
- dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
- loops, where, *pstat, stat_mask);
+ /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
+ if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000))
+ dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
+ loops, where, *pstat, stat_mask);
return loops;
}
@@ -302,6 +335,9 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd,
WARN_ON(host->cmd != NULL);
host->cmd = cmd;
+ /* Ensure, that clock are stopped else command programming and start fails */
+ imxmci_stop_clock(host);
+
if (cmd->flags & MMC_RSP_BUSY)
cmdat |= CMD_DAT_CONT_BUSY;
@@ -498,7 +534,7 @@ static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
data_error = imxmci_finish_data(host, stat);
- if (host->req->stop && (data_error == MMC_ERR_NONE)) {
+ if (host->req->stop) {
imxmci_stop_clock(host);
imxmci_start_cmd(host, host->req->stop, 0);
} else {
@@ -522,7 +558,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
int trans_done = 0;
unsigned int stat = *pstat;
- if(host->actual_bus_width == MMC_BUS_WIDTH_4)
+ if(host->actual_bus_width != MMC_BUS_WIDTH_4)
burst_len = 16;
else
burst_len = 64;
@@ -560,8 +596,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
stat = MMC_STATUS;
/* Flush extra bytes from FIFO */
- while(flush_len >= 2){
- flush_len -= 2;
+ while(flush_len && !(stat & STATUS_DATA_TRANS_DONE)){
i = MMC_BUFFER_ACCESS;
stat = MMC_STATUS;
stat &= ~STATUS_CRC_READ_ERR; /* Stupid but required there */
@@ -622,6 +657,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);;
@@ -714,10 +750,6 @@ static void imxmci_tasklet_fnc(unsigned long data)
data_dir_mask = STATUS_DATA_TRANS_DONE;
}
- imxmci_busy_wait_for_status(host, &stat,
- data_dir_mask,
- 50, "imxmci_tasklet_fnc data");
-
if(stat & data_dir_mask) {
clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
imxmci_data_done(host, stat);
@@ -775,10 +807,6 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct imxmci_host *host = mmc_priv(mmc);
int prescaler;
- dev_dbg(mmc_dev(host->mmc), "clock %u power %u vdd %u width %u\n",
- ios->clock, ios->power_mode, ios->vdd,
- (ios->bus_width==MMC_BUS_WIDTH_4)?4:1);
-
if( ios->bus_width==MMC_BUS_WIDTH_4 ) {
host->actual_bus_width = MMC_BUS_WIDTH_4;
imx_gpio_mode(PB11_PF_SD_DAT3);
@@ -837,7 +865,11 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
imxmci_stop_clock(host);
MMC_CLK_RATE = (prescaler<<3) | clk;
- imxmci_start_clock(host);
+ /*
+ * Under my understanding, clock should not be started there, because it would
+ * initiate SDHC sequencer and send last or random command into card
+ */
+ /*imxmci_start_clock(host);*/
dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);
} else {
OpenPOWER on IntegriCloud