|
@@ -1938,6 +1938,7 @@ static void dw_mci_set_drto(struct dw_mci *host)
|
|
|
unsigned int drto_clks;
|
|
|
unsigned int drto_div;
|
|
|
unsigned int drto_ms;
|
|
|
+ unsigned long irqflags;
|
|
|
|
|
|
drto_clks = mci_readl(host, TMOUT) >> 8;
|
|
|
drto_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
|
|
@@ -1949,7 +1950,11 @@ static void dw_mci_set_drto(struct dw_mci *host)
|
|
|
/* add a bit spare time */
|
|
|
drto_ms += 10;
|
|
|
|
|
|
- mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms));
|
|
|
+ spin_lock_irqsave(&host->irq_lock, irqflags);
|
|
|
+ if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events))
|
|
|
+ mod_timer(&host->dto_timer,
|
|
|
+ jiffies + msecs_to_jiffies(drto_ms));
|
|
|
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
|
|
}
|
|
|
|
|
|
static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
|
|
@@ -1970,6 +1975,18 @@ static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
|
|
|
+{
|
|
|
+ if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */
|
|
|
+ WARN_ON(del_timer_sync(&host->dto_timer));
|
|
|
+ clear_bit(EVENT_DATA_COMPLETE, &host->pending_events);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static void dw_mci_tasklet_func(unsigned long priv)
|
|
|
{
|
|
|
struct dw_mci *host = (struct dw_mci *)priv;
|
|
@@ -2111,8 +2128,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
|
/* fall through */
|
|
|
|
|
|
case STATE_DATA_BUSY:
|
|
|
- if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
|
|
|
- &host->pending_events)) {
|
|
|
+ if (!dw_mci_clear_pending_data_complete(host)) {
|
|
|
/*
|
|
|
* If data error interrupt comes but data over
|
|
|
* interrupt doesn't come within the given time.
|
|
@@ -2682,6 +2698,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|
|
}
|
|
|
|
|
|
if (pending & SDMMC_INT_DATA_OVER) {
|
|
|
+ spin_lock_irqsave(&host->irq_lock, irqflags);
|
|
|
+
|
|
|
del_timer(&host->dto_timer);
|
|
|
|
|
|
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
|
|
@@ -2694,6 +2712,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|
|
}
|
|
|
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
|
|
}
|
|
|
|
|
|
if (pending & SDMMC_INT_RXDR) {
|
|
@@ -3043,7 +3063,31 @@ exit:
|
|
|
static void dw_mci_dto_timer(unsigned long arg)
|
|
|
{
|
|
|
struct dw_mci *host = (struct dw_mci *)arg;
|
|
|
+ unsigned long irqflags;
|
|
|
+ u32 pending;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&host->irq_lock, irqflags);
|
|
|
|
|
|
+ /*
|
|
|
+ * The DTO timer is much longer than the CTO timer, so it's even less
|
|
|
+ * likely that we'll these cases, but it pays to be paranoid.
|
|
|
+ */
|
|
|
+ pending = mci_readl(host, MINTSTS); /* read-only mask reg */
|
|
|
+ if (pending & SDMMC_INT_DATA_OVER) {
|
|
|
+ /* The interrupt should fire; no need to act but we can warn */
|
|
|
+ dev_warn(host->dev, "Unexpected data interrupt latency\n");
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+ if (test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) {
|
|
|
+ /* Presumably interrupt handler couldn't delete the timer */
|
|
|
+ dev_warn(host->dev, "DTO timeout when already completed\n");
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Continued paranoia to make sure we're in the state we expect.
|
|
|
+ * This paranoia isn't really justified but it seems good to be safe.
|
|
|
+ */
|
|
|
switch (host->state) {
|
|
|
case STATE_SENDING_DATA:
|
|
|
case STATE_DATA_BUSY:
|
|
@@ -3058,8 +3102,13 @@ static void dw_mci_dto_timer(unsigned long arg)
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
break;
|
|
|
default:
|
|
|
+ dev_warn(host->dev, "Unexpected data timeout, state %d\n",
|
|
|
+ host->state);
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
+exit:
|
|
|
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_OF
|