ARM: edma: Add DT and runtime PM support to the private EDMA API
authorMatt Porter <mporter@ti.com>
Thu, 20 Jun 2013 21:06:38 +0000 (16:06 -0500)
committerSekhar Nori <nsekhar@ti.com>
Mon, 24 Jun 2013 08:38:26 +0000 (14:08 +0530)
Adds support for parsing the TI EDMA DT data into the required EDMA
private API platform data. Enables runtime PM support to initialize
the EDMA hwmod. Enables build on OMAP.

Changes by Joel:
* Setup default one-to-one mapping for queue_priority and queue_tc
mapping as discussed in [1].
* Split out xbar stuff to separate patch. [1]
* Dropped unused DT helper to convert to array
* Fixed dangling pointer issue with Sekhar's changes

[1] https://patchwork.kernel.org/patch/2226761/

Signed-off-by: Matt Porter <mporter@ti.com>
[nsekhar@ti.com: fix checkpatch errors, build breakages. Introduce
edma_setup_info_from_dt() as part of that effort]
Signed-off-by: Joel A Fernandes <joelagnel@ti.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
arch/arm/common/edma.c
arch/arm/mach-davinci/devices-da8xx.c
arch/arm/mach-davinci/devices-tnetv107x.c
arch/arm/mach-davinci/dm355.c
arch/arm/mach-davinci/dm365.c
arch/arm/mach-davinci/dm644x.c
arch/arm/mach-davinci/dm646x.c
include/linux/platform_data/edma.h

index 7658874cc3d557a53e1b39a06aeefb7bda1ce785..5183a310657cbc4455cf4a31b14d9ad720e87189 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/edma.h>
+#include <linux/err.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/platform_data/edma.h>
 
@@ -1369,13 +1376,110 @@ void edma_clear_event(unsigned channel)
 }
 EXPORT_SYMBOL(edma_clear_event);
 
-/*-----------------------------------------------------------------------*/
+#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES)
+
+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 edma_rsv_info *rsv_info;
+       s8 (*queue_tc_map)[2], (*queue_priority_map)[2];
+
+       memset(pdata, 0, sizeof(struct edma_soc_info));
+
+       ret = of_property_read_u32(node, "dma-channels", &value);
+       if (ret < 0)
+               return ret;
+       pdata->n_channel = value;
+
+       ret = of_property_read_u32(node, "ti,edma-regions", &value);
+       if (ret < 0)
+               return ret;
+       pdata->n_region = value;
+
+       ret = of_property_read_u32(node, "ti,edma-slots", &value);
+       if (ret < 0)
+               return ret;
+       pdata->n_slot = value;
+
+       pdata->n_cc = 1;
+
+       rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL);
+       if (!rsv_info)
+               return -ENOMEM;
+       pdata->rsv = rsv_info;
+
+       queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL);
+       if (!queue_tc_map)
+               return -ENOMEM;
+
+       for (i = 0; i < 3; i++) {
+               queue_tc_map[i][0] = i;
+               queue_tc_map[i][1] = i;
+       }
+       queue_tc_map[i][0] = -1;
+       queue_tc_map[i][1] = -1;
+
+       pdata->queue_tc_mapping = queue_tc_map;
+
+       queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL);
+       if (!queue_priority_map)
+               return -ENOMEM;
+
+       for (i = 0; i < 3; i++) {
+               queue_priority_map[i][0] = i;
+               queue_priority_map[i][1] = i;
+       }
+       queue_priority_map[i][0] = -1;
+       queue_priority_map[i][1] = -1;
+
+       pdata->queue_priority_mapping = queue_priority_map;
+
+       pdata->default_queue = 0;
 
-static int __init edma_probe(struct platform_device *pdev)
+       return ret;
+}
+
+static struct of_dma_filter_info edma_filter_info = {
+       .filter_fn = edma_filter_fn,
+};
+
+static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
+                                                     struct device_node *node)
+{
+       struct edma_soc_info *info;
+       int ret;
+
+       info = devm_kzalloc(dev, sizeof(struct edma_soc_info), GFP_KERNEL);
+       if (!info)
+               return ERR_PTR(-ENOMEM);
+
+       ret = edma_of_parse_dt(dev, node, info);
+       if (ret)
+               return ERR_PTR(ret);
+
+       dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap);
+       of_dma_controller_register(dev->of_node, of_dma_simple_xlate,
+                                  &edma_filter_info);
+
+       return info;
+}
+#else
+static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
+                                                     struct device_node *node)
+{
+       return ERR_PTR(-ENOSYS);
+}
+#endif
+
+static int edma_probe(struct platform_device *pdev)
 {
        struct edma_soc_info    **info = pdev->dev.platform_data;
-       const s8                (*queue_priority_mapping)[2];
-       const s8                (*queue_tc_mapping)[2];
+       struct edma_soc_info    *ninfo[EDMA_MAX_CC] = {NULL};
+       s8              (*queue_priority_mapping)[2];
+       s8              (*queue_tc_mapping)[2];
        int                     i, j, off, ln, found = 0;
        int                     status = -1;
        const s16               (*rsv_chans)[2];
@@ -1383,17 +1487,56 @@ static int __init edma_probe(struct platform_device *pdev)
        int                     irq[EDMA_MAX_CC] = {0, 0};
        int                     err_irq[EDMA_MAX_CC] = {0, 0};
        struct resource         *r[EDMA_MAX_CC] = {NULL};
+       struct resource         res[EDMA_MAX_CC];
        char                    res_name[10];
        char                    irq_name[10];
+       struct device_node      *node = pdev->dev.of_node;
+       struct device           *dev = &pdev->dev;
+       int                     ret;
+
+       if (node) {
+               /* Check if this is a second instance registered */
+               if (arch_num_cc) {
+                       dev_err(dev, "only one EDMA instance is supported via DT\n");
+                       return -ENODEV;
+               }
+
+               ninfo[0] = edma_setup_info_from_dt(dev, node);
+               if (IS_ERR(ninfo[0])) {
+                       dev_err(dev, "failed to get DT data\n");
+                       return PTR_ERR(ninfo[0]);
+               }
+
+               info = ninfo;
+       }
 
        if (!info)
                return -ENODEV;
 
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               dev_err(dev, "pm_runtime_get_sync() failed\n");
+               return ret;
+       }
+
        for (j = 0; j < EDMA_MAX_CC; j++) {
-               sprintf(res_name, "edma_cc%d", j);
-               r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+               if (!info[j]) {
+                       if (!found)
+                               return -ENODEV;
+                       break;
+               }
+               if (node) {
+                       ret = of_address_to_resource(node, j, &res[j]);
+                       if (!ret)
+                               r[j] = &res[j];
+               } else {
+                       sprintf(res_name, "edma_cc%d", j);
+                       r[j] = platform_get_resource_byname(pdev,
+                                               IORESOURCE_MEM,
                                                res_name);
-               if (!r[j] || !info[j]) {
+               }
+               if (!r[j]) {
                        if (found)
                                break;
                        else
@@ -1440,7 +1583,7 @@ static int __init edma_probe(struct platform_device *pdev)
                                        off = rsv_chans[i][0];
                                        ln = rsv_chans[i][1];
                                        clear_bits(off, ln,
-                                               edma_cc[j]->edma_unused);
+                                                 edma_cc[j]->edma_unused);
                                }
                        }
 
@@ -1456,8 +1599,13 @@ static int __init edma_probe(struct platform_device *pdev)
                        }
                }
 
-               sprintf(irq_name, "edma%d", j);
-               irq[j] = platform_get_irq_byname(pdev, irq_name);
+
+               if (node) {
+                       irq[j] = irq_of_parse_and_map(node, 0);
+               } else {
+                       sprintf(irq_name, "edma%d", j);
+                       irq[j] = platform_get_irq_byname(pdev, irq_name);
+               }
                edma_cc[j]->irq_res_start = irq[j];
                status = devm_request_irq(&pdev->dev, irq[j],
                                          dma_irq_handler, 0, "edma",
@@ -1469,8 +1617,12 @@ static int __init edma_probe(struct platform_device *pdev)
                        return status;
                }
 
-               sprintf(irq_name, "edma%d_err", j);
-               err_irq[j] = platform_get_irq_byname(pdev, irq_name);
+               if (node) {
+                       err_irq[j] = irq_of_parse_and_map(node, 2);
+               } else {
+                       sprintf(irq_name, "edma%d_err", j);
+                       err_irq[j] = platform_get_irq_byname(pdev, irq_name);
+               }
                edma_cc[j]->irq_res_end = err_irq[j];
                status = devm_request_irq(&pdev->dev, err_irq[j],
                                          dma_ccerr_handler, 0,
@@ -1516,9 +1668,17 @@ static int __init edma_probe(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id edma_of_ids[] = {
+       { .compatible = "ti,edma3", },
+       {}
+};
 
 static struct platform_driver edma_driver = {
-       .driver.name    = "edma",
+       .driver = {
+               .name   = "edma",
+               .of_match_table = edma_of_ids,
+       },
+       .probe = edma_probe,
 };
 
 static int __init edma_init(void)
index bf572525175d44896813c3f03d9f2833e1352b54..eb254fe861aca30a959ea680664287f15be7b184 100644 (file)
@@ -105,27 +105,27 @@ struct platform_device da8xx_serial_device = {
        },
 };
 
-static const s8 da8xx_queue_tc_mapping[][2] = {
+static s8 da8xx_queue_tc_mapping[][2] = {
        /* {event queue no, TC no} */
        {0, 0},
        {1, 1},
        {-1, -1}
 };
 
-static const s8 da8xx_queue_priority_mapping[][2] = {
+static s8 da8xx_queue_priority_mapping[][2] = {
        /* {event queue no, Priority} */
        {0, 3},
        {1, 7},
        {-1, -1}
 };
 
-static const s8 da850_queue_tc_mapping[][2] = {
+static s8 da850_queue_tc_mapping[][2] = {
        /* {event queue no, TC no} */
        {0, 0},
        {-1, -1}
 };
 
-static const s8 da850_queue_priority_mapping[][2] = {
+static s8 da850_queue_priority_mapping[][2] = {
        /* {event queue no, Priority} */
        {0, 3},
        {-1, -1}
index 612a0856e9c5b78437c854b6213420617cd00e7b..128cb9ae80f4c7d368c190ea380d6a4698053490 100644 (file)
 #define TNETV107X_DMACH_SDIO1_RX               28
 #define TNETV107X_DMACH_SDIO1_TX               29
 
-static const s8 edma_tc_mapping[][2] = {
+static s8 edma_tc_mapping[][2] = {
        /* event queue no       TC no   */
        {        0,              0      },
        {        1,              1      },
        {       -1,             -1      }
 };
 
-static const s8 edma_priority_mapping[][2] = {
+static s8 edma_priority_mapping[][2] = {
        /* event queue no       Prio    */
        {        0,              3      },
        {        1,              7      },
index 526cf7d06d0e4b8e7c3a203e39a1b73cb7f87943..42ef53f62c6ce44344a42ac49b71f2b8e34f6ecf 100644 (file)
@@ -569,7 +569,7 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = {
 
 /*----------------------------------------------------------------------*/
 
-static const s8
+static s8
 queue_tc_mapping[][2] = {
        /* {event queue no, TC no} */
        {0, 0},
@@ -577,7 +577,7 @@ queue_tc_mapping[][2] = {
        {-1, -1},
 };
 
-static const s8
+static s8
 queue_priority_mapping[][2] = {
        /* {event queue no, Priority} */
        {0, 3},
index c4b741173c062c78807668a55f4d6442a466ce44..fa7af5eda52d1bcf17a6120e3a603251e2b6de7f 100644 (file)
@@ -826,7 +826,7 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = {
 };
 
 /* Four Transfer Controllers on DM365 */
-static const s8
+static s8
 dm365_queue_tc_mapping[][2] = {
        /* {event queue no, TC no} */
        {0, 0},
@@ -836,7 +836,7 @@ dm365_queue_tc_mapping[][2] = {
        {-1, -1},
 };
 
-static const s8
+static s8
 dm365_queue_priority_mapping[][2] = {
        /* {event queue no, Priority} */
        {0, 7},
index dd156d58fe64d44534862b460f394cf8717f7bbe..a49d18246fe9f2e841b51d0ace2c16a4623bb069 100644 (file)
@@ -497,7 +497,7 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = {
 
 /*----------------------------------------------------------------------*/
 
-static const s8
+static s8
 queue_tc_mapping[][2] = {
        /* {event queue no, TC no} */
        {0, 0},
@@ -505,7 +505,7 @@ queue_tc_mapping[][2] = {
        {-1, -1},
 };
 
-static const s8
+static s8
 queue_priority_mapping[][2] = {
        /* {event queue no, Priority} */
        {0, 3},
index 6d52a321a8cf133b1a0eccbd82cf84bf55cc6de8..d1259e80141b0d7b85bd95cedca24fdad3e98010 100644 (file)
@@ -531,7 +531,7 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = {
 /*----------------------------------------------------------------------*/
 
 /* Four Transfer Controllers on DM646x */
-static const s8
+static s8
 dm646x_queue_tc_mapping[][2] = {
        /* {event queue no, TC no} */
        {0, 0},
@@ -541,7 +541,7 @@ dm646x_queue_tc_mapping[][2] = {
        {-1, -1},
 };
 
-static const s8
+static s8
 dm646x_queue_priority_mapping[][2] = {
        /* {event queue no, Priority} */
        {0, 4},
index 2344ea2675adef3efa02f4b94fc912a159576ec5..317f2beeaece201338246fff308b4e9100fac0ac 100644 (file)
@@ -175,8 +175,8 @@ struct edma_soc_info {
        /* Resource reservation for other cores */
        struct edma_rsv_info    *rsv;
 
-       const s8        (*queue_tc_mapping)[2];
-       const s8        (*queue_priority_mapping)[2];
+       s8      (*queue_tc_mapping)[2];
+       s8      (*queue_priority_mapping)[2];
 };
 
 #endif