diff options
author | Peter Ujfalusi <peter.ujfalusi@ti.com> | 2015-04-09 05:35:49 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2015-05-14 11:19:25 -0400 |
commit | a074ae38f859b90bd259f5df43784834b44412d1 (patch) | |
tree | 43390e9243cf41eb314b2e941c44287459f8eae4 | |
parent | 8a32222693af0edf4ad0ed2c6c4c9e383fd922dd (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/Kconfig | 4 | ||||
-rw-r--r-- | drivers/dma/Makefile | 1 | ||||
-rw-r--r-- | drivers/dma/ti-dma-crossbar.c | 188 |
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 | ||
248 | config TI_DMA_CROSSBAR | ||
249 | bool | ||
250 | |||
248 | config ARCH_HAS_ASYNC_TX_FIND_CHANNEL | 251 | config 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 | ||
334 | config DMA_BCM2835 | 338 | config 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 | |||
38 | obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o | 38 | obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o |
39 | obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o | 39 | obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o |
40 | obj-$(CONFIG_DMA_OMAP) += omap-dma.o | 40 | obj-$(CONFIG_DMA_OMAP) += omap-dma.o |
41 | obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o | ||
41 | obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o | 42 | obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o |
42 | obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o | 43 | obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o |
43 | obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o | 44 | obj-$(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 | |||
23 | static DEFINE_IDR(map_idr); | ||
24 | |||
25 | struct 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 | |||
35 | struct ti_dma_xbar_map { | ||
36 | u16 xbar_in; | ||
37 | int xbar_out; | ||
38 | }; | ||
39 | |||
40 | static inline void ti_dma_xbar_write(void __iomem *iomem, int xbar, u16 val) | ||
41 | { | ||
42 | writew_relaxed(val, iomem + (xbar * 2)); | ||
43 | } | ||
44 | |||
45 | static 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 | |||
58 | static 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 | |||
99 | static 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 | |||
171 | static const struct of_device_id ti_dma_xbar_match[] = { | ||
172 | { .compatible = "ti,dra7-dma-crossbar" }, | ||
173 | {}, | ||
174 | }; | ||
175 | |||
176 | static 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 | |||
184 | int omap_dmaxbar_init(void) | ||
185 | { | ||
186 | return platform_driver_register(&ti_dma_xbar_driver); | ||
187 | } | ||
188 | arch_initcall(omap_dmaxbar_init); | ||