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/of_address.h>
16 #include <linux/of_device.h>
17 #include <linux/of_dma.h>
19 #define TI_XBAR_DRA7 0
20 #define TI_XBAR_AM335X 1
21 static const u32 ti_xbar_type
[] = {
22 [TI_XBAR_DRA7
] = TI_XBAR_DRA7
,
23 [TI_XBAR_AM335X
] = TI_XBAR_AM335X
,
26 static const struct of_device_id ti_dma_xbar_match
[] = {
28 .compatible
= "ti,dra7-dma-crossbar",
29 .data
= &ti_xbar_type
[TI_XBAR_DRA7
],
32 .compatible
= "ti,am335x-edma-crossbar",
33 .data
= &ti_xbar_type
[TI_XBAR_AM335X
],
38 /* Crossbar on AM335x/AM437x family */
39 #define TI_AM335X_XBAR_LINES 64
41 struct ti_am335x_xbar_data
{
44 struct dma_router dmarouter
;
46 u32 xbar_events
; /* maximum number of events to select in xbar */
47 u32 dma_requests
; /* number of DMA requests on eDMA */
50 struct ti_am335x_xbar_map
{
55 static inline void ti_am335x_xbar_write(void __iomem
*iomem
, int event
, u16 val
)
57 writeb_relaxed(val
& 0x1f, iomem
+ event
);
60 static void ti_am335x_xbar_free(struct device
*dev
, void *route_data
)
62 struct ti_am335x_xbar_data
*xbar
= dev_get_drvdata(dev
);
63 struct ti_am335x_xbar_map
*map
= route_data
;
65 dev_dbg(dev
, "Unmapping XBAR event %u on channel %u\n",
66 map
->mux_val
, map
->dma_line
);
68 ti_am335x_xbar_write(xbar
->iomem
, map
->dma_line
, 0);
72 static void *ti_am335x_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
75 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
76 struct ti_am335x_xbar_data
*xbar
= platform_get_drvdata(pdev
);
77 struct ti_am335x_xbar_map
*map
;
79 if (dma_spec
->args_count
!= 3)
80 return ERR_PTR(-EINVAL
);
82 if (dma_spec
->args
[2] >= xbar
->xbar_events
) {
83 dev_err(&pdev
->dev
, "Invalid XBAR event number: %d\n",
85 return ERR_PTR(-EINVAL
);
88 if (dma_spec
->args
[0] >= xbar
->dma_requests
) {
89 dev_err(&pdev
->dev
, "Invalid DMA request line number: %d\n",
91 return ERR_PTR(-EINVAL
);
94 /* The of_node_put() will be done in the core for the node */
95 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
97 dev_err(&pdev
->dev
, "Can't get DMA master\n");
98 return ERR_PTR(-EINVAL
);
101 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
103 of_node_put(dma_spec
->np
);
104 return ERR_PTR(-ENOMEM
);
107 map
->dma_line
= (u16
)dma_spec
->args
[0];
108 map
->mux_val
= (u16
)dma_spec
->args
[2];
110 dma_spec
->args
[2] = 0;
111 dma_spec
->args_count
= 2;
113 dev_dbg(&pdev
->dev
, "Mapping XBAR event%u to DMA%u\n",
114 map
->mux_val
, map
->dma_line
);
116 ti_am335x_xbar_write(xbar
->iomem
, map
->dma_line
, map
->mux_val
);
121 static const struct of_device_id ti_am335x_master_match
[] = {
122 { .compatible
= "ti,edma3-tpcc", },
126 static int ti_am335x_xbar_probe(struct platform_device
*pdev
)
128 struct device_node
*node
= pdev
->dev
.of_node
;
129 const struct of_device_id
*match
;
130 struct device_node
*dma_node
;
131 struct ti_am335x_xbar_data
*xbar
;
132 struct resource
*res
;
139 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
143 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
145 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
149 match
= of_match_node(ti_am335x_master_match
, dma_node
);
151 dev_err(&pdev
->dev
, "DMA master is not supported\n");
152 of_node_put(dma_node
);
156 if (of_property_read_u32(dma_node
, "dma-requests",
157 &xbar
->dma_requests
)) {
159 "Missing XBAR output information, using %u.\n",
160 TI_AM335X_XBAR_LINES
);
161 xbar
->dma_requests
= TI_AM335X_XBAR_LINES
;
163 of_node_put(dma_node
);
165 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_events
)) {
167 "Missing XBAR input information, using %u.\n",
168 TI_AM335X_XBAR_LINES
);
169 xbar
->xbar_events
= TI_AM335X_XBAR_LINES
;
172 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
173 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
175 return PTR_ERR(iomem
);
179 xbar
->dmarouter
.dev
= &pdev
->dev
;
180 xbar
->dmarouter
.route_free
= ti_am335x_xbar_free
;
182 platform_set_drvdata(pdev
, xbar
);
184 /* Reset the crossbar */
185 for (i
= 0; i
< xbar
->dma_requests
; i
++)
186 ti_am335x_xbar_write(xbar
->iomem
, i
, 0);
188 ret
= of_dma_router_register(node
, ti_am335x_xbar_route_allocate
,
194 /* Crossbar on DRA7xx family */
195 #define TI_DRA7_XBAR_OUTPUTS 127
196 #define TI_DRA7_XBAR_INPUTS 256
198 struct ti_dra7_xbar_data
{
201 struct dma_router dmarouter
;
203 unsigned long *dma_inuse
;
205 u16 safe_val
; /* Value to rest the crossbar lines */
206 u32 xbar_requests
; /* number of DMA requests connected to XBAR */
207 u32 dma_requests
; /* number of DMA requests forwarded to DMA */
211 struct ti_dra7_xbar_map
{
216 static inline void ti_dra7_xbar_write(void __iomem
*iomem
, int xbar
, u16 val
)
218 writew_relaxed(val
, iomem
+ (xbar
* 2));
221 static void ti_dra7_xbar_free(struct device
*dev
, void *route_data
)
223 struct ti_dra7_xbar_data
*xbar
= dev_get_drvdata(dev
);
224 struct ti_dra7_xbar_map
*map
= route_data
;
226 dev_dbg(dev
, "Unmapping XBAR%u (was routed to %d)\n",
227 map
->xbar_in
, map
->xbar_out
);
229 ti_dra7_xbar_write(xbar
->iomem
, map
->xbar_out
, xbar
->safe_val
);
230 mutex_lock(&xbar
->mutex
);
231 clear_bit(map
->xbar_out
, xbar
->dma_inuse
);
232 mutex_unlock(&xbar
->mutex
);
236 static void *ti_dra7_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
237 struct of_dma
*ofdma
)
239 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
240 struct ti_dra7_xbar_data
*xbar
= platform_get_drvdata(pdev
);
241 struct ti_dra7_xbar_map
*map
;
243 if (dma_spec
->args
[0] >= xbar
->xbar_requests
) {
244 dev_err(&pdev
->dev
, "Invalid XBAR request number: %d\n",
246 return ERR_PTR(-EINVAL
);
249 /* The of_node_put() will be done in the core for the node */
250 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
252 dev_err(&pdev
->dev
, "Can't get DMA master\n");
253 return ERR_PTR(-EINVAL
);
256 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
258 of_node_put(dma_spec
->np
);
259 return ERR_PTR(-ENOMEM
);
262 mutex_lock(&xbar
->mutex
);
263 map
->xbar_out
= find_first_zero_bit(xbar
->dma_inuse
,
265 mutex_unlock(&xbar
->mutex
);
266 if (map
->xbar_out
== xbar
->dma_requests
) {
267 dev_err(&pdev
->dev
, "Run out of free DMA requests\n");
269 return ERR_PTR(-ENOMEM
);
271 set_bit(map
->xbar_out
, xbar
->dma_inuse
);
273 map
->xbar_in
= (u16
)dma_spec
->args
[0];
275 dma_spec
->args
[0] = map
->xbar_out
+ xbar
->dma_offset
;
277 dev_dbg(&pdev
->dev
, "Mapping XBAR%u to DMA%d\n",
278 map
->xbar_in
, map
->xbar_out
);
280 ti_dra7_xbar_write(xbar
->iomem
, map
->xbar_out
, map
->xbar_in
);
285 #define TI_XBAR_EDMA_OFFSET 0
286 #define TI_XBAR_SDMA_OFFSET 1
287 static const u32 ti_dma_offset
[] = {
288 [TI_XBAR_EDMA_OFFSET
] = 0,
289 [TI_XBAR_SDMA_OFFSET
] = 1,
292 static const struct of_device_id ti_dra7_master_match
[] = {
294 .compatible
= "ti,omap4430-sdma",
295 .data
= &ti_dma_offset
[TI_XBAR_SDMA_OFFSET
],
298 .compatible
= "ti,edma3",
299 .data
= &ti_dma_offset
[TI_XBAR_EDMA_OFFSET
],
302 .compatible
= "ti,edma3-tpcc",
303 .data
= &ti_dma_offset
[TI_XBAR_EDMA_OFFSET
],
308 static inline void ti_dra7_xbar_reserve(int offset
, int len
, unsigned long *p
)
310 for (; len
> 0; len
--)
311 set_bit(offset
+ (len
- 1), p
);
314 static int ti_dra7_xbar_probe(struct platform_device
*pdev
)
316 struct device_node
*node
= pdev
->dev
.of_node
;
317 const struct of_device_id
*match
;
318 struct device_node
*dma_node
;
319 struct ti_dra7_xbar_data
*xbar
;
320 struct property
*prop
;
321 struct resource
*res
;
330 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
334 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
336 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
340 match
= of_match_node(ti_dra7_master_match
, dma_node
);
342 dev_err(&pdev
->dev
, "DMA master is not supported\n");
343 of_node_put(dma_node
);
347 if (of_property_read_u32(dma_node
, "dma-requests",
348 &xbar
->dma_requests
)) {
350 "Missing XBAR output information, using %u.\n",
351 TI_DRA7_XBAR_OUTPUTS
);
352 xbar
->dma_requests
= TI_DRA7_XBAR_OUTPUTS
;
354 of_node_put(dma_node
);
356 xbar
->dma_inuse
= devm_kcalloc(&pdev
->dev
,
357 BITS_TO_LONGS(xbar
->dma_requests
),
358 sizeof(unsigned long), GFP_KERNEL
);
359 if (!xbar
->dma_inuse
)
362 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_requests
)) {
364 "Missing XBAR input information, using %u.\n",
365 TI_DRA7_XBAR_INPUTS
);
366 xbar
->xbar_requests
= TI_DRA7_XBAR_INPUTS
;
369 if (!of_property_read_u32(node
, "ti,dma-safe-map", &safe_val
))
370 xbar
->safe_val
= (u16
)safe_val
;
373 prop
= of_find_property(node
, "ti,reserved-dma-request-ranges", &sz
);
375 const char pname
[] = "ti,reserved-dma-request-ranges";
376 u32 (*rsv_events
)[2];
377 size_t nelm
= sz
/ sizeof(*rsv_events
);
383 rsv_events
= kcalloc(nelm
, sizeof(*rsv_events
), GFP_KERNEL
);
387 ret
= of_property_read_u32_array(node
, pname
, (u32
*)rsv_events
,
392 for (i
= 0; i
< nelm
; i
++) {
393 ti_dra7_xbar_reserve(rsv_events
[i
][0], rsv_events
[i
][1],
399 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
400 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
402 return PTR_ERR(iomem
);
406 xbar
->dmarouter
.dev
= &pdev
->dev
;
407 xbar
->dmarouter
.route_free
= ti_dra7_xbar_free
;
408 xbar
->dma_offset
= *(u32
*)match
->data
;
410 mutex_init(&xbar
->mutex
);
411 platform_set_drvdata(pdev
, xbar
);
413 /* Reset the crossbar */
414 for (i
= 0; i
< xbar
->dma_requests
; i
++) {
415 if (!test_bit(i
, xbar
->dma_inuse
))
416 ti_dra7_xbar_write(xbar
->iomem
, i
, xbar
->safe_val
);
419 ret
= of_dma_router_register(node
, ti_dra7_xbar_route_allocate
,
422 /* Restore the defaults for the crossbar */
423 for (i
= 0; i
< xbar
->dma_requests
; i
++) {
424 if (!test_bit(i
, xbar
->dma_inuse
))
425 ti_dra7_xbar_write(xbar
->iomem
, i
, i
);
432 static int ti_dma_xbar_probe(struct platform_device
*pdev
)
434 const struct of_device_id
*match
;
437 match
= of_match_node(ti_dma_xbar_match
, pdev
->dev
.of_node
);
438 if (unlikely(!match
))
441 switch (*(u32
*)match
->data
) {
443 ret
= ti_dra7_xbar_probe(pdev
);
446 ret
= ti_am335x_xbar_probe(pdev
);
449 dev_err(&pdev
->dev
, "Unsupported crossbar\n");
457 static struct platform_driver ti_dma_xbar_driver
= {
459 .name
= "ti-dma-crossbar",
460 .of_match_table
= of_match_ptr(ti_dma_xbar_match
),
462 .probe
= ti_dma_xbar_probe
,
465 static int omap_dmaxbar_init(void)
467 return platform_driver_register(&ti_dma_xbar_driver
);
469 arch_initcall(omap_dmaxbar_init
);