|
@@ -25,6 +25,13 @@
|
|
|
#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)
|