ARM: edma: Add EDMA crossbar event mux support
authorMatt Porter <mporter@ti.com>
Thu, 20 Jun 2013 21:06:39 +0000 (16:06 -0500)
committerSekhar Nori <nsekhar@ti.com>
Mon, 24 Jun 2013 08:38:34 +0000 (14:08 +0530)
EDMA supports a cross bar which provides ability
to mux additional events into physical channels
present in the channel controller.

This is required when the number of events present
in the system are more than number of available
physical channels.

Changes by Joel:
* Split EDMA xbar support out of original EDMA DT parsing patch
to keep it easier for review.
* Rewrite shift and offset calculation.

Suggested-by: Sekhar Nori <nsekhar@ti.com>
Suggested by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Joel A Fernandes <joelagnel@ti.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
[nsekhar@ti.com: fix checkpatch errors and a minor coding improvement]
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
arch/arm/common/edma.c
include/linux/platform_data/edma.h

index 5183a310657cbc4455cf4a31b14d9ad720e87189..a432e6c1dac18fdf33d1b5d85bfb6c1b7e6cfed0 100644 (file)
@@ -1378,12 +1378,76 @@ EXPORT_SYMBOL(edma_clear_event);
 
 #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES)
 
+static int edma_of_read_u32_to_s16_array(const struct device_node *np,
+                                        const char *propname, s16 *out_values,
+                                        size_t sz)
+{
+       int ret;
+
+       ret = of_property_read_u16_array(np, propname, out_values, sz);
+       if (ret)
+               return ret;
+
+       /* Terminate it */
+       *out_values++ = -1;
+       *out_values++ = -1;
+
+       return 0;
+}
+
+static int edma_xbar_event_map(struct device *dev,
+                              struct device_node *node,
+                              struct edma_soc_info *pdata, int len)
+{
+       int ret, i;
+       struct resource res;
+       void __iomem *xbar;
+       const s16 (*xbar_chans)[2];
+       u32 shift, offset, mux;
+
+       xbar_chans = devm_kzalloc(dev,
+                                 len/sizeof(s16) + 2*sizeof(s16),
+                                 GFP_KERNEL);
+       if (!xbar_chans)
+               return -ENOMEM;
+
+       ret = of_address_to_resource(node, 1, &res);
+       if (ret)
+               return -EIO;
+
+       xbar = devm_ioremap(dev, res.start, resource_size(&res));
+       if (!xbar)
+               return -ENOMEM;
+
+       ret = edma_of_read_u32_to_s16_array(node,
+                                           "ti,edma-xbar-event-map",
+                                           (s16 *)xbar_chans,
+                                           len/sizeof(u32));
+       if (ret)
+               return -EIO;
+
+       for (i = 0; xbar_chans[i][0] != -1; i++) {
+               shift = (xbar_chans[i][1] & 0x03) << 3;
+               offset = xbar_chans[i][1] & 0xfffffffc;
+               mux = readl(xbar + offset);
+               mux &= ~(0xff << shift);
+               mux |= xbar_chans[i][0] << shift;
+               writel(mux, (xbar + offset));
+       }
+
+       pdata->xbar_chans = xbar_chans;
+
+       return 0;
+}
+
 static int edma_of_parse_dt(struct device *dev,
                            struct device_node *node,
                            struct edma_soc_info *pdata)
 {
        int ret = 0, i;
        u32 value;
+       struct property *prop;
+       size_t sz;
        struct edma_rsv_info *rsv_info;
        s8 (*queue_tc_map)[2], (*queue_priority_map)[2];
 
@@ -1439,6 +1503,10 @@ static int edma_of_parse_dt(struct device *dev,
 
        pdata->default_queue = 0;
 
+       prop = of_find_property(node, "ti,edma-xbar-event-map", &sz);
+       if (prop)
+               ret = edma_xbar_event_map(dev, node, pdata, sz);
+
        return ret;
 }
 
@@ -1484,6 +1552,7 @@ static int edma_probe(struct platform_device *pdev)
        int                     status = -1;
        const s16               (*rsv_chans)[2];
        const s16               (*rsv_slots)[2];
+       const s16               (*xbar_chans)[2];
        int                     irq[EDMA_MAX_CC] = {0, 0};
        int                     err_irq[EDMA_MAX_CC] = {0, 0};
        struct resource         *r[EDMA_MAX_CC] = {NULL};
@@ -1599,6 +1668,15 @@ static int edma_probe(struct platform_device *pdev)
                        }
                }
 
+               /* Clear the xbar mapped channels in unused list */
+               xbar_chans = info[j]->xbar_chans;
+               if (xbar_chans) {
+                       for (i = 0; xbar_chans[i][1] != -1; i++) {
+                               off = xbar_chans[i][1];
+                               clear_bits(off, 1,
+                                          edma_cc[j]->edma_unused);
+                       }
+               }
 
                if (node) {
                        irq[j] = irq_of_parse_and_map(node, 0);
index 317f2beeaece201338246fff308b4e9100fac0ac..57300fd7cc03435a55c8824309c52594e926e66f 100644 (file)
@@ -177,6 +177,7 @@ struct edma_soc_info {
 
        s8      (*queue_tc_mapping)[2];
        s8      (*queue_priority_mapping)[2];
+       const s16       (*xbar_chans)[2];
 };
 
 #endif