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
22 static const struct of_device_id ti_dma_xbar_match
[] = {
24 .compatible
= "ti,dra7-dma-crossbar",
25 .data
= (void *)TI_XBAR_DRA7
,
28 .compatible
= "ti,am335x-edma-crossbar",
29 .data
= (void *)TI_XBAR_AM335X
,
34 /* Crossbar on AM335x/AM437x family */
35 #define TI_AM335X_XBAR_LINES 64
37 struct ti_am335x_xbar_data
{
40 struct dma_router dmarouter
;
42 u32 xbar_events
; /* maximum number of events to select in xbar */
43 u32 dma_requests
; /* number of DMA requests on eDMA */
46 struct ti_am335x_xbar_map
{
51 static inline void ti_am335x_xbar_write(void __iomem
*iomem
, int event
, u16 val
)
53 writeb_relaxed(val
& 0x1f, iomem
+ event
);
56 static void ti_am335x_xbar_free(struct device
*dev
, void *route_data
)
58 struct ti_am335x_xbar_data
*xbar
= dev_get_drvdata(dev
);
59 struct ti_am335x_xbar_map
*map
= route_data
;
61 dev_dbg(dev
, "Unmapping XBAR event %u on channel %u\n",
62 map
->mux_val
, map
->dma_line
);
64 ti_am335x_xbar_write(xbar
->iomem
, map
->dma_line
, 0);
68 static void *ti_am335x_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
71 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
72 struct ti_am335x_xbar_data
*xbar
= platform_get_drvdata(pdev
);
73 struct ti_am335x_xbar_map
*map
;
75 if (dma_spec
->args_count
!= 3)
76 return ERR_PTR(-EINVAL
);
78 if (dma_spec
->args
[2] >= xbar
->xbar_events
) {
79 dev_err(&pdev
->dev
, "Invalid XBAR event number: %d\n",
81 return ERR_PTR(-EINVAL
);
84 if (dma_spec
->args
[0] >= xbar
->dma_requests
) {
85 dev_err(&pdev
->dev
, "Invalid DMA request line number: %d\n",
87 return ERR_PTR(-EINVAL
);
90 /* The of_node_put() will be done in the core for the node */
91 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
93 dev_err(&pdev
->dev
, "Can't get DMA master\n");
94 return ERR_PTR(-EINVAL
);
97 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
99 of_node_put(dma_spec
->np
);
100 return ERR_PTR(-ENOMEM
);
103 map
->dma_line
= (u16
)dma_spec
->args
[0];
104 map
->mux_val
= (u16
)dma_spec
->args
[2];
106 dma_spec
->args
[2] = 0;
107 dma_spec
->args_count
= 2;
109 dev_dbg(&pdev
->dev
, "Mapping XBAR event%u to DMA%u\n",
110 map
->mux_val
, map
->dma_line
);
112 ti_am335x_xbar_write(xbar
->iomem
, map
->dma_line
, map
->mux_val
);
117 static const struct of_device_id ti_am335x_master_match
[] = {
118 { .compatible
= "ti,edma3-tpcc", },
122 static int ti_am335x_xbar_probe(struct platform_device
*pdev
)
124 struct device_node
*node
= pdev
->dev
.of_node
;
125 const struct of_device_id
*match
;
126 struct device_node
*dma_node
;
127 struct ti_am335x_xbar_data
*xbar
;
128 struct resource
*res
;
135 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
139 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
141 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
145 match
= of_match_node(ti_am335x_master_match
, dma_node
);
147 dev_err(&pdev
->dev
, "DMA master is not supported\n");
151 if (of_property_read_u32(dma_node
, "dma-requests",
152 &xbar
->dma_requests
)) {
154 "Missing XBAR output information, using %u.\n",
155 TI_AM335X_XBAR_LINES
);
156 xbar
->dma_requests
= TI_AM335X_XBAR_LINES
;
158 of_node_put(dma_node
);
160 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_events
)) {
162 "Missing XBAR input information, using %u.\n",
163 TI_AM335X_XBAR_LINES
);
164 xbar
->xbar_events
= TI_AM335X_XBAR_LINES
;
167 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
168 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
170 return PTR_ERR(iomem
);
174 xbar
->dmarouter
.dev
= &pdev
->dev
;
175 xbar
->dmarouter
.route_free
= ti_am335x_xbar_free
;
177 platform_set_drvdata(pdev
, xbar
);
179 /* Reset the crossbar */
180 for (i
= 0; i
< xbar
->dma_requests
; i
++)
181 ti_am335x_xbar_write(xbar
->iomem
, i
, 0);
183 ret
= of_dma_router_register(node
, ti_am335x_xbar_route_allocate
,
189 /* Crossbar on DRA7xx family */
190 #define TI_DRA7_XBAR_OUTPUTS 127
191 #define TI_DRA7_XBAR_INPUTS 256
193 #define TI_XBAR_EDMA_OFFSET 0
194 #define TI_XBAR_SDMA_OFFSET 1
196 struct ti_dra7_xbar_data
{
199 struct dma_router dmarouter
;
201 unsigned long *dma_inuse
;
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 mutex_lock(&xbar
->mutex
);
229 clear_bit(map
->xbar_out
, xbar
->dma_inuse
);
230 mutex_unlock(&xbar
->mutex
);
234 static void *ti_dra7_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
235 struct of_dma
*ofdma
)
237 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
238 struct ti_dra7_xbar_data
*xbar
= platform_get_drvdata(pdev
);
239 struct ti_dra7_xbar_map
*map
;
241 if (dma_spec
->args
[0] >= xbar
->xbar_requests
) {
242 dev_err(&pdev
->dev
, "Invalid XBAR request number: %d\n",
244 return ERR_PTR(-EINVAL
);
247 /* The of_node_put() will be done in the core for the node */
248 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
250 dev_err(&pdev
->dev
, "Can't get DMA master\n");
251 return ERR_PTR(-EINVAL
);
254 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
256 of_node_put(dma_spec
->np
);
257 return ERR_PTR(-ENOMEM
);
260 mutex_lock(&xbar
->mutex
);
261 map
->xbar_out
= find_first_zero_bit(xbar
->dma_inuse
,
263 mutex_unlock(&xbar
->mutex
);
264 if (map
->xbar_out
== xbar
->dma_requests
) {
265 dev_err(&pdev
->dev
, "Run out of free DMA requests\n");
267 return ERR_PTR(-ENOMEM
);
269 set_bit(map
->xbar_out
, xbar
->dma_inuse
);
271 map
->xbar_in
= (u16
)dma_spec
->args
[0];
273 dma_spec
->args
[0] = map
->xbar_out
+ xbar
->dma_offset
;
275 dev_dbg(&pdev
->dev
, "Mapping XBAR%u to DMA%d\n",
276 map
->xbar_in
, map
->xbar_out
);
278 ti_dra7_xbar_write(xbar
->iomem
, map
->xbar_out
, map
->xbar_in
);
283 static const struct of_device_id ti_dra7_master_match
[] = {
285 .compatible
= "ti,omap4430-sdma",
286 .data
= (void *)TI_XBAR_SDMA_OFFSET
,
289 .compatible
= "ti,edma3",
290 .data
= (void *)TI_XBAR_EDMA_OFFSET
,
293 .compatible
= "ti,edma3-tpcc",
294 .data
= (void *)TI_XBAR_EDMA_OFFSET
,
299 static inline void ti_dra7_xbar_reserve(int offset
, int len
, unsigned long *p
)
301 for (; len
> 0; len
--)
302 clear_bit(offset
+ (len
- 1), p
);
305 static int ti_dra7_xbar_probe(struct platform_device
*pdev
)
307 struct device_node
*node
= pdev
->dev
.of_node
;
308 const struct of_device_id
*match
;
309 struct device_node
*dma_node
;
310 struct ti_dra7_xbar_data
*xbar
;
311 struct property
*prop
;
312 struct resource
*res
;
321 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
325 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
327 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
331 match
= of_match_node(ti_dra7_master_match
, dma_node
);
333 dev_err(&pdev
->dev
, "DMA master is not supported\n");
337 if (of_property_read_u32(dma_node
, "dma-requests",
338 &xbar
->dma_requests
)) {
340 "Missing XBAR output information, using %u.\n",
341 TI_DRA7_XBAR_OUTPUTS
);
342 xbar
->dma_requests
= TI_DRA7_XBAR_OUTPUTS
;
344 of_node_put(dma_node
);
346 xbar
->dma_inuse
= devm_kcalloc(&pdev
->dev
,
347 BITS_TO_LONGS(xbar
->dma_requests
),
348 sizeof(unsigned long), GFP_KERNEL
);
349 if (!xbar
->dma_inuse
)
352 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_requests
)) {
354 "Missing XBAR input information, using %u.\n",
355 TI_DRA7_XBAR_INPUTS
);
356 xbar
->xbar_requests
= TI_DRA7_XBAR_INPUTS
;
359 if (!of_property_read_u32(node
, "ti,dma-safe-map", &safe_val
))
360 xbar
->safe_val
= (u16
)safe_val
;
363 prop
= of_find_property(node
, "ti,reserved-dma-request-ranges", &sz
);
365 const char pname
[] = "ti,reserved-dma-request-ranges";
366 u32 (*rsv_events
)[2];
367 size_t nelm
= sz
/ sizeof(*rsv_events
);
373 rsv_events
= kcalloc(nelm
, sizeof(*rsv_events
), GFP_KERNEL
);
377 ret
= of_property_read_u32_array(node
, pname
, (u32
*)rsv_events
,
382 for (i
= 0; i
< nelm
; i
++) {
383 ti_dra7_xbar_reserve(rsv_events
[i
][0], rsv_events
[i
][1],
389 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
390 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
392 return PTR_ERR(iomem
);
396 xbar
->dmarouter
.dev
= &pdev
->dev
;
397 xbar
->dmarouter
.route_free
= ti_dra7_xbar_free
;
398 xbar
->dma_offset
= (u32
)match
->data
;
400 mutex_init(&xbar
->mutex
);
401 platform_set_drvdata(pdev
, xbar
);
403 /* Reset the crossbar */
404 for (i
= 0; i
< xbar
->dma_requests
; i
++) {
405 if (!test_bit(i
, xbar
->dma_inuse
))
406 ti_dra7_xbar_write(xbar
->iomem
, i
, xbar
->safe_val
);
409 ret
= of_dma_router_register(node
, ti_dra7_xbar_route_allocate
,
412 /* Restore the defaults for the crossbar */
413 for (i
= 0; i
< xbar
->dma_requests
; i
++) {
414 if (!test_bit(i
, xbar
->dma_inuse
))
415 ti_dra7_xbar_write(xbar
->iomem
, i
, i
);
422 static int ti_dma_xbar_probe(struct platform_device
*pdev
)
424 const struct of_device_id
*match
;
427 match
= of_match_node(ti_dma_xbar_match
, pdev
->dev
.of_node
);
428 if (unlikely(!match
))
431 switch ((u32
)match
->data
) {
433 ret
= ti_dra7_xbar_probe(pdev
);
436 ret
= ti_am335x_xbar_probe(pdev
);
439 dev_err(&pdev
->dev
, "Unsupported crossbar\n");
447 static struct platform_driver ti_dma_xbar_driver
= {
449 .name
= "ti-dma-crossbar",
450 .of_match_table
= of_match_ptr(ti_dma_xbar_match
),
452 .probe
= ti_dma_xbar_probe
,
455 int omap_dmaxbar_init(void)
457 return platform_driver_register(&ti_dma_xbar_driver
);
459 arch_initcall(omap_dmaxbar_init
);