aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@ti.com>2015-04-09 05:35:49 -0400
committerVinod Koul <vinod.koul@intel.com>2015-05-14 11:19:25 -0400
commita074ae38f859b90bd259f5df43784834b44412d1 (patch)
tree43390e9243cf41eb314b2e941c44287459f8eae4
parent8a32222693af0edf4ad0ed2c6c4c9e383fd922dd (diff)
dmaengine: Add driver for TI DMA crossbar on DRA7x
The DRA7x has more peripherals with DMA requests than the sDMA can handle: 205 vs 127. All DMA requests are routed through the DMA crossbar, which can be configured to route selected incoming DMA requests to specific sDMA request. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r--drivers/dma/Kconfig4
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/ti-dma-crossbar.c188
3 files changed, 193 insertions, 0 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index fd7ac13f2574..681651aed320 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -245,6 +245,9 @@ config TI_EDMA
245 Enable support for the TI EDMA controller. This DMA 245 Enable support for the TI EDMA controller. This DMA
246 engine is found on TI DaVinci and AM33xx parts. 246 engine is found on TI DaVinci and AM33xx parts.
247 247
248config TI_DMA_CROSSBAR
249 bool
250
248config ARCH_HAS_ASYNC_TX_FIND_CHANNEL 251config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
249 bool 252 bool
250 253
@@ -330,6 +333,7 @@ config DMA_OMAP
330 depends on ARCH_OMAP 333 depends on ARCH_OMAP
331 select DMA_ENGINE 334 select DMA_ENGINE
332 select DMA_VIRTUAL_CHANNELS 335 select DMA_VIRTUAL_CHANNELS
336 select TI_DMA_CROSSBAR if SOC_DRA7XX
333 337
334config DMA_BCM2835 338config DMA_BCM2835
335 tristate "BCM2835 DMA engine support" 339 tristate "BCM2835 DMA engine support"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 69f77d5ba53b..535919559f12 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
38obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o 38obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
39obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o 39obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
40obj-$(CONFIG_DMA_OMAP) += omap-dma.o 40obj-$(CONFIG_DMA_OMAP) += omap-dma.o
41obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
41obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o 42obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
42obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o 43obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
43obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o 44obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
new file mode 100644
index 000000000000..24f5ca2356bf
--- /dev/null
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -0,0 +1,188 @@
1/*
2 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
3 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 */
10#include <linux/slab.h>
11#include <linux/err.h>
12#include <linux/init.h>
13#include <linux/list.h>
14#include <linux/io.h>
15#include <linux/idr.h>
16#include <linux/of_address.h>
17#include <linux/of_device.h>
18#include <linux/of_dma.h>
19
20#define TI_XBAR_OUTPUTS 127
21#define TI_XBAR_INPUTS 256
22
23static DEFINE_IDR(map_idr);
24
25struct ti_dma_xbar_data {
26 void __iomem *iomem;
27
28 struct dma_router dmarouter;
29
30 u16 safe_val; /* Value to rest the crossbar lines */
31 u32 xbar_requests; /* number of DMA requests connected to XBAR */
32 u32 dma_requests; /* number of DMA requests forwarded to DMA */
33};
34
35struct ti_dma_xbar_map {
36 u16 xbar_in;
37 int xbar_out;
38};
39
40static inline void ti_dma_xbar_write(void __iomem *iomem, int xbar, u16 val)
41{
42 writew_relaxed(val, iomem + (xbar * 2));
43}
44
45static void ti_dma_xbar_free(struct device *dev, void *route_data)
46{
47 struct ti_dma_xbar_data *xbar = dev_get_drvdata(dev);
48 struct ti_dma_xbar_map *map = route_data;
49
50 dev_dbg(dev, "Unmapping XBAR%u (was routed to %d)\n",
51 map->xbar_in, map->xbar_out);
52
53 ti_dma_xbar_write(xbar->iomem, map->xbar_out, xbar->safe_val);
54 idr_remove(&map_idr, map->xbar_out);
55 kfree(map);
56}
57
58static void *ti_dma_xbar_route_allocate(struct of_phandle_args *dma_spec,
59 struct of_dma *ofdma)
60{
61 struct platform_device *pdev = of_find_device_by_node(ofdma->of_node);
62 struct ti_dma_xbar_data *xbar = platform_get_drvdata(pdev);
63 struct ti_dma_xbar_map *map;
64
65 if (dma_spec->args[0] >= xbar->xbar_requests) {
66 dev_err(&pdev->dev, "Invalid XBAR request number: %d\n",
67 dma_spec->args[0]);
68 return ERR_PTR(-EINVAL);
69 }
70
71 /* The of_node_put() will be done in the core for the node */
72 dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0);
73 if (!dma_spec->np) {
74 dev_err(&pdev->dev, "Can't get DMA master\n");
75 return ERR_PTR(-EINVAL);
76 }
77
78 map = kzalloc(sizeof(*map), GFP_KERNEL);
79 if (!map) {
80 of_node_put(dma_spec->np);
81 return ERR_PTR(-ENOMEM);
82 }
83
84 map->xbar_out = idr_alloc(&map_idr, NULL, 0, xbar->dma_requests,
85 GFP_KERNEL);
86 map->xbar_in = (u16)dma_spec->args[0];
87
88 /* The DMA request is 1 based in sDMA */
89 dma_spec->args[0] = map->xbar_out + 1;
90
91 dev_dbg(&pdev->dev, "Mapping XBAR%u to DMA%d\n",
92 map->xbar_in, map->xbar_out);
93
94 ti_dma_xbar_write(xbar->iomem, map->xbar_out, map->xbar_in);
95
96 return map;
97}
98
99static int ti_dma_xbar_probe(struct platform_device *pdev)
100{
101 struct device_node *node = pdev->dev.of_node;
102 struct device_node *dma_node;
103 struct ti_dma_xbar_data *xbar;
104 struct resource *res;
105 u32 safe_val;
106 void __iomem *iomem;
107 int i, ret;
108
109 if (!node)
110 return -ENODEV;
111
112 xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL);
113 if (!xbar)
114 return -ENOMEM;
115
116 dma_node = of_parse_phandle(node, "dma-masters", 0);
117 if (!dma_node) {
118 dev_err(&pdev->dev, "Can't get DMA master node\n");
119 return -ENODEV;
120 }
121
122 if (of_property_read_u32(dma_node, "dma-requests",
123 &xbar->dma_requests)) {
124 dev_info(&pdev->dev,
125 "Missing XBAR output information, using %u.\n",
126 TI_XBAR_OUTPUTS);
127 xbar->dma_requests = TI_XBAR_OUTPUTS;
128 }
129 of_node_put(dma_node);
130
131 if (of_property_read_u32(node, "dma-requests", &xbar->xbar_requests)) {
132 dev_info(&pdev->dev,
133 "Missing XBAR input information, using %u.\n",
134 TI_XBAR_INPUTS);
135 xbar->xbar_requests = TI_XBAR_INPUTS;
136 }
137
138 if (!of_property_read_u32(node, "ti,dma-safe-map", &safe_val))
139 xbar->safe_val = (u16)safe_val;
140
141 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
142 if (!res)
143 return -ENODEV;
144
145 iomem = devm_ioremap_resource(&pdev->dev, res);
146 if (!iomem)
147 return -ENOMEM;
148
149 xbar->iomem = iomem;
150
151 xbar->dmarouter.dev = &pdev->dev;
152 xbar->dmarouter.route_free = ti_dma_xbar_free;
153
154 platform_set_drvdata(pdev, xbar);
155
156 /* Reset the crossbar */
157 for (i = 0; i < xbar->dma_requests; i++)
158 ti_dma_xbar_write(xbar->iomem, i, xbar->safe_val);
159
160 ret = of_dma_router_register(node, ti_dma_xbar_route_allocate,
161 &xbar->dmarouter);
162 if (ret) {
163 /* Restore the defaults for the crossbar */
164 for (i = 0; i < xbar->dma_requests; i++)
165 ti_dma_xbar_write(xbar->iomem, i, i);
166 }
167
168 return ret;
169}
170
171static const struct of_device_id ti_dma_xbar_match[] = {
172 { .compatible = "ti,dra7-dma-crossbar" },
173 {},
174};
175
176static struct platform_driver ti_dma_xbar_driver = {
177 .driver = {
178 .name = "ti-dma-crossbar",
179 .of_match_table = of_match_ptr(ti_dma_xbar_match),
180 },
181 .probe = ti_dma_xbar_probe,
182};
183
184int omap_dmaxbar_init(void)
185{
186 return platform_driver_register(&ti_dma_xbar_driver);
187}
188arch_initcall(omap_dmaxbar_init);