Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gerg/m68knommu
[sfrench/cifs-2.6.git] / drivers / mmc / host / dw_mmc.c
index 4f2806720c5c355aad0f7c87a3c28ff641c25a4e..0aa39975f33b8fbf36f0995cb56ffe283c7ca1b2 100644 (file)
@@ -817,7 +817,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host,
        struct dma_slave_config cfg;
        struct dma_async_tx_descriptor *desc = NULL;
        struct scatterlist *sgl = host->data->sg;
-       const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
+       static const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
        u32 sg_elems = host->data->sg_len;
        u32 fifoth_val;
        u32 fifo_offset = host->fifo_reg - host->regs;
@@ -1024,7 +1024,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
 static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
 {
        unsigned int blksz = data->blksz;
-       const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
+       static const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
        u32 fifo_width = 1 << host->data_shift;
        u32 blksz_depth = blksz / fifo_width, fifoth_val;
        u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
@@ -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) {
@@ -2791,7 +2811,7 @@ static int dw_mci_init_slot(struct dw_mci *host)
 
        /*if there are external regulators, get them*/
        ret = mmc_regulator_get_supply(mmc);
-       if (ret == -EPROBE_DEFER)
+       if (ret)
                goto err_host_allocated;
 
        if (!mmc->ocr_avail)
@@ -2971,9 +2991,9 @@ no_dma:
        host->use_dma = TRANS_MODE_PIO;
 }
 
-static void dw_mci_cmd11_timer(unsigned long arg)
+static void dw_mci_cmd11_timer(struct timer_list *t)
 {
-       struct dw_mci *host = (struct dw_mci *)arg;
+       struct dw_mci *host = from_timer(host, t, cmd11_timer);
 
        if (host->state != STATE_SENDING_CMD11) {
                dev_warn(host->dev, "Unexpected CMD11 timeout\n");
@@ -2985,9 +3005,9 @@ static void dw_mci_cmd11_timer(unsigned long arg)
        tasklet_schedule(&host->tasklet);
 }
 
-static void dw_mci_cto_timer(unsigned long arg)
+static void dw_mci_cto_timer(struct timer_list *t)
 {
-       struct dw_mci *host = (struct dw_mci *)arg;
+       struct dw_mci *host = from_timer(host, t, cto_timer);
        unsigned long irqflags;
        u32 pending;
 
@@ -3040,10 +3060,34 @@ exit:
        spin_unlock_irqrestore(&host->irq_lock, irqflags);
 }
 
-static void dw_mci_dto_timer(unsigned long arg)
+static void dw_mci_dto_timer(struct timer_list *t)
 {
-       struct dw_mci *host = (struct dw_mci *)arg;
+       struct dw_mci *host = from_timer(host, t, dto_timer);
+       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
@@ -3208,14 +3257,9 @@ int dw_mci_probe(struct dw_mci *host)
                }
        }
 
-       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);
+       timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0);
+       timer_setup(&host->cto_timer, dw_mci_cto_timer, 0);
+       timer_setup(&host->dto_timer, dw_mci_dto_timer, 0);
 
        spin_lock_init(&host->lock);
        spin_lock_init(&host->irq_lock);