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_OUTPUTS 127
21 #define TI_XBAR_INPUTS 256
23 #define TI_XBAR_EDMA_OFFSET 0
24 #define TI_XBAR_SDMA_OFFSET 1
26 struct ti_dma_xbar_data
{
29 struct dma_router dmarouter
;
32 u16 safe_val
; /* Value to rest the crossbar lines */
33 u32 xbar_requests
; /* number of DMA requests connected to XBAR */
34 u32 dma_requests
; /* number of DMA requests forwarded to DMA */
38 struct ti_dma_xbar_map
{
43 static inline void ti_dma_xbar_write(void __iomem
*iomem
, int xbar
, u16 val
)
45 writew_relaxed(val
, iomem
+ (xbar
* 2));
48 static void ti_dma_xbar_free(struct device
*dev
, void *route_data
)
50 struct ti_dma_xbar_data
*xbar
= dev_get_drvdata(dev
);
51 struct ti_dma_xbar_map
*map
= route_data
;
53 dev_dbg(dev
, "Unmapping XBAR%u (was routed to %d)\n",
54 map
->xbar_in
, map
->xbar_out
);
56 ti_dma_xbar_write(xbar
->iomem
, map
->xbar_out
, xbar
->safe_val
);
57 idr_remove(&xbar
->map_idr
, map
->xbar_out
);
61 static void *ti_dma_xbar_route_allocate(struct of_phandle_args
*dma_spec
,
64 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
65 struct ti_dma_xbar_data
*xbar
= platform_get_drvdata(pdev
);
66 struct ti_dma_xbar_map
*map
;
68 if (dma_spec
->args
[0] >= xbar
->xbar_requests
) {
69 dev_err(&pdev
->dev
, "Invalid XBAR request number: %d\n",
71 return ERR_PTR(-EINVAL
);
74 /* The of_node_put() will be done in the core for the node */
75 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
77 dev_err(&pdev
->dev
, "Can't get DMA master\n");
78 return ERR_PTR(-EINVAL
);
81 map
= kzalloc(sizeof(*map
), GFP_KERNEL
);
83 of_node_put(dma_spec
->np
);
84 return ERR_PTR(-ENOMEM
);
87 map
->xbar_out
= idr_alloc(&xbar
->map_idr
, NULL
, 0, xbar
->dma_requests
,
89 map
->xbar_in
= (u16
)dma_spec
->args
[0];
91 dma_spec
->args
[0] = map
->xbar_out
+ xbar
->dma_offset
;
93 dev_dbg(&pdev
->dev
, "Mapping XBAR%u to DMA%d\n",
94 map
->xbar_in
, map
->xbar_out
);
96 ti_dma_xbar_write(xbar
->iomem
, map
->xbar_out
, map
->xbar_in
);
101 static const struct of_device_id ti_dma_master_match
[] = {
103 .compatible
= "ti,omap4430-sdma",
104 .data
= (void *)TI_XBAR_SDMA_OFFSET
,
107 .compatible
= "ti,edma3",
108 .data
= (void *)TI_XBAR_EDMA_OFFSET
,
113 static int ti_dma_xbar_probe(struct platform_device
*pdev
)
115 struct device_node
*node
= pdev
->dev
.of_node
;
116 const struct of_device_id
*match
;
117 struct device_node
*dma_node
;
118 struct ti_dma_xbar_data
*xbar
;
119 struct resource
*res
;
127 xbar
= devm_kzalloc(&pdev
->dev
, sizeof(*xbar
), GFP_KERNEL
);
131 idr_init(&xbar
->map_idr
);
133 dma_node
= of_parse_phandle(node
, "dma-masters", 0);
135 dev_err(&pdev
->dev
, "Can't get DMA master node\n");
139 match
= of_match_node(ti_dma_master_match
, dma_node
);
141 dev_err(&pdev
->dev
, "DMA master is not supported\n");
145 if (of_property_read_u32(dma_node
, "dma-requests",
146 &xbar
->dma_requests
)) {
148 "Missing XBAR output information, using %u.\n",
150 xbar
->dma_requests
= TI_XBAR_OUTPUTS
;
152 of_node_put(dma_node
);
154 if (of_property_read_u32(node
, "dma-requests", &xbar
->xbar_requests
)) {
156 "Missing XBAR input information, using %u.\n",
158 xbar
->xbar_requests
= TI_XBAR_INPUTS
;
161 if (!of_property_read_u32(node
, "ti,dma-safe-map", &safe_val
))
162 xbar
->safe_val
= (u16
)safe_val
;
164 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
165 iomem
= devm_ioremap_resource(&pdev
->dev
, res
);
167 return PTR_ERR(iomem
);
171 xbar
->dmarouter
.dev
= &pdev
->dev
;
172 xbar
->dmarouter
.route_free
= ti_dma_xbar_free
;
173 xbar
->dma_offset
= (u32
)match
->data
;
175 platform_set_drvdata(pdev
, xbar
);
177 /* Reset the crossbar */
178 for (i
= 0; i
< xbar
->dma_requests
; i
++)
179 ti_dma_xbar_write(xbar
->iomem
, i
, xbar
->safe_val
);
181 ret
= of_dma_router_register(node
, ti_dma_xbar_route_allocate
,
184 /* Restore the defaults for the crossbar */
185 for (i
= 0; i
< xbar
->dma_requests
; i
++)
186 ti_dma_xbar_write(xbar
->iomem
, i
, i
);
192 static const struct of_device_id ti_dma_xbar_match
[] = {
193 { .compatible
= "ti,dra7-dma-crossbar" },
197 static struct platform_driver ti_dma_xbar_driver
= {
199 .name
= "ti-dma-crossbar",
200 .of_match_table
= of_match_ptr(ti_dma_xbar_match
),
202 .probe
= ti_dma_xbar_probe
,
205 int omap_dmaxbar_init(void)
207 return platform_driver_register(&ti_dma_xbar_driver
);
209 arch_initcall(omap_dmaxbar_init
);