ARM: OMAP: dma: Fix the dma_chan_link_map init order
[pandora-kernel.git] / arch / arm / plat-omap / dma.c
index 002fb4d..e3e3559 100644 (file)
@@ -563,22 +563,25 @@ EXPORT_SYMBOL(omap_set_dma_dest_burst_mode);
 
 static inline void omap_enable_channel_irq(int lch)
 {
-       u32 status;
-
        /* Clear CSR */
        if (cpu_class_is_omap1())
-               status = p->dma_read(CSR, lch);
-       else if (cpu_class_is_omap2())
+               p->dma_read(CSR, lch);
+       else
                p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
 
        /* Enable some nice interrupts. */
        p->dma_write(dma_chan[lch].enabled_irqs, CICR, lch);
 }
 
-static void omap_disable_channel_irq(int lch)
+static inline void omap_disable_channel_irq(int lch)
 {
-       if (cpu_class_is_omap2())
-               p->dma_write(0, CICR, lch);
+       /* disable channel interrupts */
+       p->dma_write(0, CICR, lch);
+       /* Clear CSR */
+       if (cpu_class_is_omap1())
+               p->dma_read(CSR, lch);
+       else
+               p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
 }
 
 void omap_enable_dma_irq(int lch, u16 bits)
@@ -622,14 +625,14 @@ static inline void disable_lnk(int lch)
        l = p->dma_read(CLNK_CTRL, lch);
 
        /* Disable interrupts */
+       omap_disable_channel_irq(lch);
+
        if (cpu_class_is_omap1()) {
-               p->dma_write(0, CICR, lch);
                /* Set the STOP_LNK bit */
                l |= 1 << 14;
        }
 
        if (cpu_class_is_omap2()) {
-               omap_disable_channel_irq(lch);
                /* Clear the ENABLE_LNK bit */
                l &= ~(1 << 15);
        }
@@ -647,6 +650,9 @@ static inline void omap2_enable_irq_lch(int lch)
                return;
 
        spin_lock_irqsave(&dma_chan_lock, flags);
+       /* clear IRQ STATUS */
+       p->dma_write(1 << lch, IRQSTATUS_L0, lch);
+       /* Enable interrupt */
        val = p->dma_read(IRQENABLE_L0, lch);
        val |= 1 << lch;
        p->dma_write(val, IRQENABLE_L0, lch);
@@ -662,9 +668,12 @@ static inline void omap2_disable_irq_lch(int lch)
                return;
 
        spin_lock_irqsave(&dma_chan_lock, flags);
+       /* Disable interrupt */
        val = p->dma_read(IRQENABLE_L0, lch);
        val &= ~(1 << lch);
        p->dma_write(val, IRQENABLE_L0, lch);
+       /* clear IRQ STATUS */
+       p->dma_write(1 << lch, IRQSTATUS_L0, lch);
        spin_unlock_irqrestore(&dma_chan_lock, flags);
 }
 
@@ -680,8 +689,8 @@ int omap_request_dma(int dev_id, const char *dev_name,
        for (ch = 0; ch < dma_chan_count; ch++) {
                if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
                        free_ch = ch;
-                       if (dev_id == 0)
-                               break;
+                       /* Exit after first free channel found */
+                       break;
                }
        }
        if (free_ch == -1) {
@@ -735,11 +744,8 @@ int omap_request_dma(int dev_id, const char *dev_name,
        }
 
        if (cpu_class_is_omap2()) {
-               omap2_enable_irq_lch(free_ch);
                omap_enable_channel_irq(free_ch);
-               /* Clear the CSR register and IRQ status register */
-               p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, free_ch);
-               p->dma_write(1 << free_ch, IRQSTATUS_L0, 0);
+               omap2_enable_irq_lch(free_ch);
        }
 
        *dma_ch_out = free_ch;
@@ -758,27 +764,19 @@ void omap_free_dma(int lch)
                return;
        }
 
-       if (cpu_class_is_omap1()) {
-               /* Disable all DMA interrupts for the channel. */
-               p->dma_write(0, CICR, lch);
-               /* Make sure the DMA transfer is stopped. */
-               p->dma_write(0, CCR, lch);
-       }
-
-       if (cpu_class_is_omap2()) {
+       /* Disable interrupt for logical channel */
+       if (cpu_class_is_omap2())
                omap2_disable_irq_lch(lch);
 
-               /* Clear the CSR register and IRQ status register */
-               p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
-               p->dma_write(1 << lch, IRQSTATUS_L0, lch);
+       /* Disable all DMA interrupts for the channel. */
+       omap_disable_channel_irq(lch);
 
-               /* Disable all DMA interrupts for the channel. */
-               p->dma_write(0, CICR, lch);
+       /* Make sure the DMA transfer is stopped. */
+       p->dma_write(0, CCR, lch);
 
-               /* Make sure the DMA transfer is stopped. */
-               p->dma_write(0, CCR, lch);
+       /* Clear registers */
+       if (cpu_class_is_omap2())
                omap_clear_dma(lch);
-       }
 
        spin_lock_irqsave(&dma_chan_lock, flags);
        dma_chan[lch].dev_id = -1;
@@ -884,11 +882,12 @@ void omap_start_dma(int lch)
                int next_lch, cur_lch;
                char dma_chan_link_map[dma_lch_count];
 
-               dma_chan_link_map[lch] = 1;
                /* Set the link register of the first channel */
                enable_lnk(lch);
 
                memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
+               dma_chan_link_map[lch] = 1;
+
                cur_lch = dma_chan[lch].next_lch;
                do {
                        next_lch = dma_chan[cur_lch].next_lch;
@@ -915,6 +914,13 @@ void omap_start_dma(int lch)
                        l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
        l |= OMAP_DMA_CCR_EN;
 
+       /*
+        * As dma_write() uses IO accessors which are weakly ordered, there
+        * is no guarantee that data in coherent DMA memory will be visible
+        * to the DMA device.  Add a memory barrier here to ensure that any
+        * such data is visible prior to enabling DMA.
+        */
+       mb();
        p->dma_write(l, CCR, lch);
 
        dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
@@ -926,8 +932,7 @@ void omap_stop_dma(int lch)
        u32 l;
 
        /* Disable all interrupts on the channel */
-       if (cpu_class_is_omap1())
-               p->dma_write(0, CICR, lch);
+       omap_disable_channel_irq(lch);
 
        l = p->dma_read(CCR, lch);
        if (IS_DMA_ERRATA(DMA_ERRATA_i541) &&
@@ -964,6 +969,13 @@ void omap_stop_dma(int lch)
                p->dma_write(l, CCR, lch);
        }
 
+       /*
+        * Ensure that data transferred by DMA is visible to any access
+        * after DMA has been disabled.  This is important for coherent
+        * DMA regions.
+        */
+       mb();
+
        if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
                int next_lch, cur_lch = lch;
                char dma_chan_link_map[dma_lch_count];
@@ -2002,7 +2014,7 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
        errata                  = p->errata;
 
        if ((d->dev_caps & RESERVE_CHANNEL) && omap_dma_reserve_channels
-                       && (omap_dma_reserve_channels <= dma_lch_count))
+                       && (omap_dma_reserve_channels < d->lch_count))
                d->lch_count    = omap_dma_reserve_channels;
 
        dma_lch_count           = d->lch_count;