Merge branch 'davinci-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / arch / arm / mach-davinci / dma.c
index 648fbb760ae1d63c74e700eae260629046add8b1..53137387aee18b69a35526dacea7d9b0e1267f3c 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/slab.h>
 
 #include <mach/edma.h>
 
@@ -226,11 +227,11 @@ struct edma {
         */
        DECLARE_BITMAP(edma_inuse, EDMA_MAX_PARAMENTRY);
 
-       /* The edma_noevent bit for each channel is clear unless
-        * it doesn't trigger DMA events on this platform.  It uses a
-        * bit of SOC-specific initialization code.
+       /* The edma_unused bit for each channel is clear unless
+        * it is not being used on this platform. It uses a bit
+        * of SOC-specific initialization code.
         */
-       DECLARE_BITMAP(edma_noevent, EDMA_MAX_DMACH);
+       DECLARE_BITMAP(edma_unused, EDMA_MAX_DMACH);
 
        unsigned        irq_res_start;
        unsigned        irq_res_end;
@@ -243,6 +244,7 @@ struct edma {
 };
 
 static struct edma *edma_info[EDMA_MAX_CC];
+static int arch_num_cc;
 
 /* dummy param set used to (re)initialize parameter RAM slots */
 static const struct edmacc_param dummy_paramset = {
@@ -555,8 +557,27 @@ static int reserve_contiguous_slots(int ctlr, unsigned int id,
        return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1);
 }
 
+static int prepare_unused_channel_list(struct device *dev, void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       int i, ctlr;
+
+       for (i = 0; i < pdev->num_resources; i++) {
+               if ((pdev->resource[i].flags & IORESOURCE_DMA) &&
+                               (int)pdev->resource[i].start >= 0) {
+                       ctlr = EDMA_CTLR(pdev->resource[i].start);
+                       clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start),
+                                       edma_info[ctlr]->edma_unused);
+               }
+       }
+
+       return 0;
+}
+
 /*-----------------------------------------------------------------------*/
 
+static bool unused_chan_list_done;
+
 /* Resource alloc/free:  dma channels, parameter RAM slots */
 
 /**
@@ -594,7 +615,22 @@ int edma_alloc_channel(int channel,
                void *data,
                enum dma_event_q eventq_no)
 {
-       unsigned i, done, ctlr = 0;
+       unsigned i, done = 0, ctlr = 0;
+       int ret = 0;
+
+       if (!unused_chan_list_done) {
+               /*
+                * Scan all the platform devices to find out the EDMA channels
+                * used and clear them in the unused list, making the rest
+                * available for ARM usage.
+                */
+               ret = bus_for_each_dev(&platform_bus_type, NULL, NULL,
+                               prepare_unused_channel_list);
+               if (ret < 0)
+                       return ret;
+
+               unused_chan_list_done = true;
+       }
 
        if (channel >= 0) {
                ctlr = EDMA_CTLR(channel);
@@ -602,15 +638,15 @@ int edma_alloc_channel(int channel,
        }
 
        if (channel < 0) {
-               for (i = 0; i < EDMA_MAX_CC; i++) {
+               for (i = 0; i < arch_num_cc; i++) {
                        channel = 0;
                        for (;;) {
                                channel = find_next_bit(edma_info[i]->
-                                               edma_noevent,
+                                               edma_unused,
                                                edma_info[i]->num_channels,
                                                channel);
                                if (channel == edma_info[i]->num_channels)
-                                       return -ENOMEM;
+                                       break;
                                if (!test_and_set_bit(channel,
                                                edma_info[i]->edma_inuse)) {
                                        done = 1;
@@ -622,6 +658,8 @@ int edma_alloc_channel(int channel,
                        if (done)
                                break;
                }
+               if (!done)
+                       return -ENOMEM;
        } else if (channel >= edma_info[ctlr]->num_channels) {
                return -EINVAL;
        } else if (test_and_set_bit(channel, edma_info[ctlr]->edma_inuse)) {
@@ -642,7 +680,7 @@ int edma_alloc_channel(int channel,
 
        map_dmach_queue(ctlr, channel, eventq_no);
 
-       return channel;
+       return EDMA_CTLR_CHAN(ctlr, channel);
 }
 EXPORT_SYMBOL(edma_alloc_channel);
 
@@ -1219,7 +1257,7 @@ int edma_start(unsigned channel)
                unsigned int mask = (1 << (channel & 0x1f));
 
                /* EDMA channels without event association */
-               if (test_bit(channel, edma_info[ctlr]->edma_noevent)) {
+               if (test_bit(channel, edma_info[ctlr]->edma_unused)) {
                        pr_debug("EDMA: ESR%d %08x\n", j,
                                edma_shadow0_read_array(ctlr, SH_ESR, j));
                        edma_shadow0_write_array(ctlr, SH_ESR, j, mask);
@@ -1229,7 +1267,8 @@ int edma_start(unsigned channel)
                /* EDMA channel with event association */
                pr_debug("EDMA: ER%d %08x\n", j,
                        edma_shadow0_read_array(ctlr, SH_ER, j));
-               /* Clear any pending error */
+               /* Clear any pending event or error */
+               edma_write_array(ctlr, EDMA_ECR, j, mask);
                edma_write_array(ctlr, EDMA_EMCR, j, mask);
                /* Clear any SER */
                edma_shadow0_write_array(ctlr, SH_SECR, j, mask);
@@ -1344,7 +1383,6 @@ static int __init edma_probe(struct platform_device *pdev)
        const s8                (*queue_tc_mapping)[2];
        int                     i, j, found = 0;
        int                     status = -1;
-       const s8                *noevent;
        int                     irq[EDMA_MAX_CC] = {0, 0};
        int                     err_irq[EDMA_MAX_CC] = {0, 0};
        struct resource         *r[EDMA_MAX_CC] = {NULL};
@@ -1407,11 +1445,9 @@ static int __init edma_probe(struct platform_device *pdev)
                        memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i),
                                        &dummy_paramset, PARM_SIZE);
 
-               noevent = info[j].noevent;
-               if (noevent) {
-                       while (*noevent != -1)
-                               set_bit(*noevent++, edma_info[j]->edma_noevent);
-               }
+               /* Mark all channels as unused */
+               memset(edma_info[j]->edma_unused, 0xff,
+                       sizeof(edma_info[j]->edma_unused));
 
                sprintf(irq_name, "edma%d", j);
                irq[j] = platform_get_irq_byname(pdev, irq_name);
@@ -1467,6 +1503,7 @@ static int __init edma_probe(struct platform_device *pdev)
                        edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
                        edma_write_array(j, EDMA_QRAE, i, 0x0);
                }
+               arch_num_cc++;
        }
 
        if (tc_errs_handled) {