return cmdr;
}
+static inline void dw_mci_set_cto(struct dw_mci *host)
+{
+ unsigned int cto_clks;
+ unsigned int cto_ms;
+
+ cto_clks = mci_readl(host, TMOUT) & 0xff;
+ cto_ms = DIV_ROUND_UP(cto_clks, host->bus_hz / 1000);
+
+ /* add a bit spare time */
+ cto_ms += 10;
+
+ mod_timer(&host->cto_timer,
+ jiffies + msecs_to_jiffies(cto_ms) + 1);
+}
+
static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
wmb(); /* drain writebuffer */
dw_mci_wait_while_busy(host, cmd_flags);
+ /* response expected command only */
+ if (cmd_flags & SDMMC_CMD_RESP_EXP)
+ dw_mci_set_cto(host);
+
mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
}
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+ del_timer(&host->cto_timer);
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending;
smp_wmb(); /* drain writebuffer */
}
if (pending & SDMMC_INT_CMD_DONE) {
+ del_timer(&host->cto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
dw_mci_cmd_interrupt(host, pending);
}
tasklet_schedule(&host->tasklet);
}
+static void dw_mci_cto_timer(unsigned long arg)
+{
+ struct dw_mci *host = (struct dw_mci *)arg;
+
+ switch (host->state) {
+ case STATE_SENDING_CMD11:
+ case STATE_SENDING_CMD:
+ case STATE_SENDING_STOP:
+ /*
+ * If CMD_DONE interrupt does NOT come in sending command
+ * state, we should notify the driver to terminate current
+ * transfer and report a command timeout to the core.
+ */
+ host->cmd_status = SDMMC_INT_RTO;
+ set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+ tasklet_schedule(&host->tasklet);
+ break;
+ default:
+ dev_warn(host->dev, "Unexpected command timeout, state %d\n",
+ host->state);
+ break;
+ }
+}
+
static void dw_mci_dto_timer(unsigned long arg)
{
struct dw_mci *host = (struct dw_mci *)arg;
setup_timer(&host->cmd11_timer,
dw_mci_cmd11_timer, (unsigned long)host);
+ setup_timer(&host->cto_timer,
+ dw_mci_cto_timer, (unsigned long)host);
+
setup_timer(&host->dto_timer,
dw_mci_dto_timer, (unsigned long)host);