2 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
3 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
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.
10 #include <linux/slab.h>
11 #include <linux/err.h>
12 #include <linux/init.h>
13 #include <linux/list.h>
15 #include <linux/idr.h>
16 #include <linux/of_address.h>
17 #include <linux/of_device.h>
18 #include <linux/of_dma.h>
20 #define TI_XBAR_DRA7 0
21 #define TI_XBAR_AM335X 1
23 static const struct of_device_id ti_dma_xbar_match
[] = {
25 .compatible
= "ti,dra7-dma-crossbar",
26 .data
= (void *)TI_XBAR_DRA7
,
29 .compatible
= "ti,am335x-edma-crossbar",
30 .data
= (void *)TI_XBAR_AM335X
,
35 /* Crossbar on AM335x/AM437x family */
36 #define TI_AM335X_XBAR_LINES 64
38 struct ti_am335x_xbar_data
{
41 struct dma_router dmarouter
;
43 u32 xbar_events
; /* maximum number of events to select in xbar */
44 u32 dma_requests
; /* number of DMA requests on eDMA */
47 struct ti_am335x_xbar_map
{
52 static inline void ti_am335x_xbar_write(void __iomem
*iomem
, int event
, u16 val
)
54 writeb_relaxed(val
& 0x1f, iomem
+ event
);
57 static void ti_am335x_xbar_free(struct device
*dev
, void *route_data
)
59 struct ti_am335x_xbar_data
*xbar
= dev_get_drvdata(dev
);
60 struct ti_am335x_xbar_map
*map
= route_data
;
62 dev_dbg(dev
, "Unmapping XBAR event %u on channel %u\n",
63 map
->mux_val
, map
->dma_line
);
65 ti_am335x_xbar_write(xbar
->iomem
, map
->dma_line
, 0);
69 static void *ti_am335x_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
72 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
73 struct ti_am335x_xbar_data
*xbar
= platform_get_drvdata(pdev
);
74 struct ti_am335x_xbar_map
*map
;
76 if (dma_spec
->args_count
!= 3)
77 return ERR_PTR(-EINVAL
);
79 if (dma_spec
->args
[2] >= xbar
->xbar_events
) {
80 dev_err(&pdev
->dev
, "Invalid XBAR event number: %d\n",
82 return ERR_PTR(-EINVAL
);
85 if (dma_spec
->args
[0] >= xbar
->dma_requests
) {
86 dev_err(&pdev
->dev
, "Invalid DMA request line number: %d\n",
88 return ERR_PTR(-EINVAL
);
91 /* The of_node_put() will be done in the core for the node */
92 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
94 dev_err(&pdev
->dev
, "Can't get DMA master\n");
95 return ERR_PTR(-EINVAL
);
98 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
100 of_node_put(dma_spec
->np
);
101 return ERR_PTR(-ENOMEM
);
104 map
->dma_line
= (u16
)dma_spec
->args
[0];
105 map
->mux_val
= (u16
)dma_spec
->args
[2];
107 dma_spec
->args
[2] = 0;
108 dma_spec
->args_count
= 2;
110 dev_dbg(&pdev
->dev
, "Mapping XBAR event%u to DMA%u\n",
111 map
->mux_val
, map
->dma_line
);
113 ti_am335x_xbar_write(xbar
->iomem
, map
->dma_line
, map
->mux_val
);
118 static const struct of_device_id ti_am335x_master_match
[] = {
119 { .compatible
= "ti,edma3-tpcc", },
123 static int ti_am335x_xbar_probe(struct platform_device
*pdev
)
125 struct device_node
*node
= pdev
->dev
.of_node
;
126 const struct of_device_id
*match
;
127 struct device_node
*dma_node
;
128 struct ti_am335x_xbar_data
*xbar
;
129 struct resource
*res
;
136 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
140 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
142 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
146 match
= of_match_node(ti_am335x_master_match
, dma_node
);
148 dev_err(&pdev
->dev
, "DMA master is not supported\n");
152 if (of_property_read_u32(dma_node
, "dma-requests",
153 &xbar
->dma_requests
)) {
155 "Missing XBAR output information, using %u.\n",
156 TI_AM335X_XBAR_LINES
);
157 xbar
->dma_requests
= TI_AM335X_XBAR_LINES
;
159 of_node_put(dma_node
);
161 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_events
)) {
163 "Missing XBAR input information, using %u.\n",
164 TI_AM335X_XBAR_LINES
);
165 xbar
->xbar_events
= TI_AM335X_XBAR_LINES
;
168 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
169 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
171 return PTR_ERR(iomem
);
175 xbar
->dmarouter
.dev
= &pdev
->dev
;
176 xbar
->dmarouter
.route_free
= ti_am335x_xbar_free
;
178 platform_set_drvdata(pdev
, xbar
);
180 /* Reset the crossbar */
181 for (i
= 0; i
< xbar
->dma_requests
; i
++)
182 ti_am335x_xbar_write(xbar
->iomem
, i
, 0);
184 ret
= of_dma_router_register(node
, ti_am335x_xbar_route_allocate
,
190 /* Crossbar on DRA7xx family */
191 #define TI_DRA7_XBAR_OUTPUTS 127
192 #define TI_DRA7_XBAR_INPUTS 256
194 #define TI_XBAR_EDMA_OFFSET 0
195 #define TI_XBAR_SDMA_OFFSET 1
197 struct ti_dra7_xbar_data
{
200 struct dma_router dmarouter
;
203 u16 safe_val
; /* Value to rest the crossbar lines */
204 u32 xbar_requests
; /* number of DMA requests connected to XBAR */
205 u32 dma_requests
; /* number of DMA requests forwarded to DMA */
209 struct ti_dra7_xbar_map
{
214 static inline void ti_dra7_xbar_write(void __iomem
*iomem
, int xbar
, u16 val
)
216 writew_relaxed(val
, iomem
+ (xbar
* 2));
219 static void ti_dra7_xbar_free(struct device
*dev
, void *route_data
)
221 struct ti_dra7_xbar_data
*xbar
= dev_get_drvdata(dev
);
222 struct ti_dra7_xbar_map
*map
= route_data
;
224 dev_dbg(dev
, "Unmapping XBAR%u (was routed to %d)\n",
225 map
->xbar_in
, map
->xbar_out
);
227 ti_dra7_xbar_write(xbar
->iomem
, map
->xbar_out
, xbar
->safe_val
);
228 idr_remove(&xbar
->map_idr
, map
->xbar_out
);
232 static void *ti_dra7_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
233 struct of_dma
*ofdma
)
235 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
236 struct ti_dra7_xbar_data
*xbar
= platform_get_drvdata(pdev
);
237 struct ti_dra7_xbar_map
*map
;
239 if (dma_spec
->args
[0] >= xbar
->xbar_requests
) {
240 dev_err(&pdev
->dev
, "Invalid XBAR request number: %d\n",
242 return ERR_PTR(-EINVAL
);
245 /* The of_node_put() will be done in the core for the node */
246 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
248 dev_err(&pdev
->dev
, "Can't get DMA master\n");
249 return ERR_PTR(-EINVAL
);
252 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
254 of_node_put(dma_spec
->np
);
255 return ERR_PTR(-ENOMEM
);
258 map
->xbar_out
= idr_alloc(&xbar
->map_idr
, NULL
, 0, xbar
->dma_requests
,
260 map
->xbar_in
= (u16
)dma_spec
->args
[0];
262 dma_spec
->args
[0] = map
->xbar_out
+ xbar
->dma_offset
;
264 dev_dbg(&pdev
->dev
, "Mapping XBAR%u to DMA%d\n",
265 map
->xbar_in
, map
->xbar_out
);
267 ti_dra7_xbar_write(xbar
->iomem
, map
->xbar_out
, map
->xbar_in
);
272 static const struct of_device_id ti_dra7_master_match
[] = {
274 .compatible
= "ti,omap4430-sdma",
275 .data
= (void *)TI_XBAR_SDMA_OFFSET
,
278 .compatible
= "ti,edma3",
279 .data
= (void *)TI_XBAR_EDMA_OFFSET
,
284 static int ti_dra7_xbar_probe(struct platform_device
*pdev
)
286 struct device_node
*node
= pdev
->dev
.of_node
;
287 const struct of_device_id
*match
;
288 struct device_node
*dma_node
;
289 struct ti_dra7_xbar_data
*xbar
;
290 struct resource
*res
;
298 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
302 idr_init(&xbar
->map_idr
);
304 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
306 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
310 match
= of_match_node(ti_dra7_master_match
, dma_node
);
312 dev_err(&pdev
->dev
, "DMA master is not supported\n");
316 if (of_property_read_u32(dma_node
, "dma-requests",
317 &xbar
->dma_requests
)) {
319 "Missing XBAR output information, using %u.\n",
320 TI_DRA7_XBAR_OUTPUTS
);
321 xbar
->dma_requests
= TI_DRA7_XBAR_OUTPUTS
;
323 of_node_put(dma_node
);
325 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_requests
)) {
327 "Missing XBAR input information, using %u.\n",
328 TI_DRA7_XBAR_INPUTS
);
329 xbar
->xbar_requests
= TI_DRA7_XBAR_INPUTS
;
332 if (!of_property_read_u32(node
, "ti,dma-safe-map", &safe_val
))
333 xbar
->safe_val
= (u16
)safe_val
;
335 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
336 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
338 return PTR_ERR(iomem
);
342 xbar
->dmarouter
.dev
= &pdev
->dev
;
343 xbar
->dmarouter
.route_free
= ti_dra7_xbar_free
;
344 xbar
->dma_offset
= (u32
)match
->data
;
346 platform_set_drvdata(pdev
, xbar
);
348 /* Reset the crossbar */
349 for (i
= 0; i
< xbar
->dma_requests
; i
++)
350 ti_dra7_xbar_write(xbar
->iomem
, i
, xbar
->safe_val
);
352 ret
= of_dma_router_register(node
, ti_dra7_xbar_route_allocate
,
355 /* Restore the defaults for the crossbar */
356 for (i
= 0; i
< xbar
->dma_requests
; i
++)
357 ti_dra7_xbar_write(xbar
->iomem
, i
, i
);
363 static int ti_dma_xbar_probe(struct platform_device
*pdev
)
365 const struct of_device_id
*match
;
368 match
= of_match_node(ti_dma_xbar_match
, pdev
->dev
.of_node
);
369 if (unlikely(!match
))
372 switch ((u32
)match
->data
) {
374 ret
= ti_dra7_xbar_probe(pdev
);
377 ret
= ti_am335x_xbar_probe(pdev
);
380 dev_err(&pdev
->dev
, "Unsupported crossbar\n");
388 static struct platform_driver ti_dma_xbar_driver
= {
390 .name
= "ti-dma-crossbar",
391 .of_match_table
= of_match_ptr(ti_dma_xbar_match
),
393 .probe
= ti_dma_xbar_probe
,
396 int omap_dmaxbar_init(void)
398 return platform_driver_register(&ti_dma_xbar_driver
);
400 arch_initcall(omap_dmaxbar_init
);