diff options
author | Vinod Koul <vinod.koul@intel.com> | 2015-06-24 23:51:43 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2015-06-24 23:51:43 -0400 |
commit | 0e0fa66e39db6b2c72dbc0d8975fc2a45533a8eb (patch) | |
tree | 1b538ba40d301ba70a645d405e37e74bb5d70119 | |
parent | 9324fdf5267b12f6db660fe52e882bbfffcc109a (diff) | |
parent | a074ae38f859b90bd259f5df43784834b44412d1 (diff) |
Merge branch 'topic/omap' into for-linus
-rw-r--r-- | Documentation/devicetree/bindings/dma/dma.txt | 28 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt | 52 | ||||
-rw-r--r-- | drivers/dma/Kconfig | 4 | ||||
-rw-r--r-- | drivers/dma/Makefile | 1 | ||||
-rw-r--r-- | drivers/dma/dmaengine.c | 7 | ||||
-rw-r--r-- | drivers/dma/of-dma.c | 89 | ||||
-rw-r--r-- | drivers/dma/omap-dma.c | 29 | ||||
-rw-r--r-- | drivers/dma/ti-dma-crossbar.c | 188 | ||||
-rw-r--r-- | include/linux/dmaengine.h | 17 | ||||
-rw-r--r-- | include/linux/of_dma.h | 21 |
10 files changed, 430 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt index 82104271e754..6312fb00ce8d 100644 --- a/Documentation/devicetree/bindings/dma/dma.txt +++ b/Documentation/devicetree/bindings/dma/dma.txt | |||
@@ -31,6 +31,34 @@ Example: | |||
31 | dma-requests = <127>; | 31 | dma-requests = <127>; |
32 | }; | 32 | }; |
33 | 33 | ||
34 | * DMA router | ||
35 | |||
36 | DMA routers are transparent IP blocks used to route DMA request lines from | ||
37 | devices to the DMA controller. Some SoCs (like TI DRA7x) have more peripherals | ||
38 | integrated with DMA requests than what the DMA controller can handle directly. | ||
39 | |||
40 | Required property: | ||
41 | - dma-masters: phandle of the DMA controller or list of phandles for | ||
42 | the DMA controllers the router can direct the signal to. | ||
43 | - #dma-cells: Must be at least 1. Used to provide DMA router specific | ||
44 | information. See DMA client binding below for more | ||
45 | details. | ||
46 | |||
47 | Optional properties: | ||
48 | - dma-requests: Number of incoming request lines the router can handle. | ||
49 | - In the node pointed by the dma-masters: | ||
50 | - dma-requests: The router driver might need to look for this in order | ||
51 | to configure the routing. | ||
52 | |||
53 | Example: | ||
54 | sdma_xbar: dma-router@4a002b78 { | ||
55 | compatible = "ti,dra7-dma-crossbar"; | ||
56 | reg = <0x4a002b78 0xfc>; | ||
57 | #dma-cells = <1>; | ||
58 | dma-requests = <205>; | ||
59 | ti,dma-safe-map = <0>; | ||
60 | dma-masters = <&sdma>; | ||
61 | }; | ||
34 | 62 | ||
35 | * DMA client | 63 | * DMA client |
36 | 64 | ||
diff --git a/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt b/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt new file mode 100644 index 000000000000..63a48928f3a8 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt | |||
@@ -0,0 +1,52 @@ | |||
1 | Texas Instruments DMA Crossbar (DMA request router) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "ti,dra7-dma-crossbar" for DRA7xx DMA crossbar | ||
5 | - reg: Memory map for accessing module | ||
6 | - #dma-cells: Should be set to <1>. | ||
7 | Clients should use the crossbar request number (input) | ||
8 | - dma-requests: Number of DMA requests the crossbar can receive | ||
9 | - dma-masters: phandle pointing to the DMA controller | ||
10 | |||
11 | The DMA controller node need to have the following poroperties: | ||
12 | - dma-requests: Number of DMA requests the controller can handle | ||
13 | |||
14 | Optional properties: | ||
15 | - ti,dma-safe-map: Safe routing value for unused request lines | ||
16 | |||
17 | Example: | ||
18 | |||
19 | /* DMA controller */ | ||
20 | sdma: dma-controller@4a056000 { | ||
21 | compatible = "ti,omap4430-sdma"; | ||
22 | reg = <0x4a056000 0x1000>; | ||
23 | interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>, | ||
24 | <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>, | ||
25 | <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>, | ||
26 | <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>; | ||
27 | #dma-cells = <1>; | ||
28 | dma-channels = <32>; | ||
29 | dma-requests = <127>; | ||
30 | }; | ||
31 | |||
32 | /* DMA crossbar */ | ||
33 | sdma_xbar: dma-router@4a002b78 { | ||
34 | compatible = "ti,dra7-dma-crossbar"; | ||
35 | reg = <0x4a002b78 0xfc>; | ||
36 | #dma-cells = <1>; | ||
37 | dma-requests = <205>; | ||
38 | ti,dma-safe-map = <0>; | ||
39 | dma-masters = <&sdma>; | ||
40 | }; | ||
41 | |||
42 | /* DMA client */ | ||
43 | uart1: serial@4806a000 { | ||
44 | compatible = "ti,omap4-uart"; | ||
45 | reg = <0x4806a000 0x100>; | ||
46 | interrupts-extended = <&gic GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; | ||
47 | ti,hwmods = "uart1"; | ||
48 | clock-frequency = <48000000>; | ||
49 | status = "disabled"; | ||
50 | dmas = <&sdma_xbar 49>, <&sdma_xbar 50>; | ||
51 | dma-names = "tx", "rx"; | ||
52 | }; | ||
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index bda2cb06dc7a..0205ade0ba32 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/dmaengine.c b/drivers/dma/dmaengine.c index c0793818bb99..052a2bd1a5cf 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c | |||
@@ -267,6 +267,13 @@ static void dma_chan_put(struct dma_chan *chan) | |||
267 | /* This channel is not in use anymore, free it */ | 267 | /* This channel is not in use anymore, free it */ |
268 | if (!chan->client_count && chan->device->device_free_chan_resources) | 268 | if (!chan->client_count && chan->device->device_free_chan_resources) |
269 | chan->device->device_free_chan_resources(chan); | 269 | chan->device->device_free_chan_resources(chan); |
270 | |||
271 | /* If the channel is used via a DMA request router, free the mapping */ | ||
272 | if (chan->router && chan->router->route_free) { | ||
273 | chan->router->route_free(chan->router->dev, chan->route_data); | ||
274 | chan->router = NULL; | ||
275 | chan->route_data = NULL; | ||
276 | } | ||
270 | } | 277 | } |
271 | 278 | ||
272 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) | 279 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) |
diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index cbd4a8aff120..1e1f2986eba8 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c | |||
@@ -45,6 +45,50 @@ static struct of_dma *of_dma_find_controller(struct of_phandle_args *dma_spec) | |||
45 | } | 45 | } |
46 | 46 | ||
47 | /** | 47 | /** |
48 | * of_dma_router_xlate - translation function for router devices | ||
49 | * @dma_spec: pointer to DMA specifier as found in the device tree | ||
50 | * @of_dma: pointer to DMA controller data (router information) | ||
51 | * | ||
52 | * The function creates new dma_spec to be passed to the router driver's | ||
53 | * of_dma_route_allocate() function to prepare a dma_spec which will be used | ||
54 | * to request channel from the real DMA controller. | ||
55 | */ | ||
56 | static struct dma_chan *of_dma_router_xlate(struct of_phandle_args *dma_spec, | ||
57 | struct of_dma *ofdma) | ||
58 | { | ||
59 | struct dma_chan *chan; | ||
60 | struct of_dma *ofdma_target; | ||
61 | struct of_phandle_args dma_spec_target; | ||
62 | void *route_data; | ||
63 | |||
64 | /* translate the request for the real DMA controller */ | ||
65 | memcpy(&dma_spec_target, dma_spec, sizeof(dma_spec_target)); | ||
66 | route_data = ofdma->of_dma_route_allocate(&dma_spec_target, ofdma); | ||
67 | if (IS_ERR(route_data)) | ||
68 | return NULL; | ||
69 | |||
70 | ofdma_target = of_dma_find_controller(&dma_spec_target); | ||
71 | if (!ofdma_target) | ||
72 | return NULL; | ||
73 | |||
74 | chan = ofdma_target->of_dma_xlate(&dma_spec_target, ofdma_target); | ||
75 | if (chan) { | ||
76 | chan->router = ofdma->dma_router; | ||
77 | chan->route_data = route_data; | ||
78 | } else { | ||
79 | ofdma->dma_router->route_free(ofdma->dma_router->dev, | ||
80 | route_data); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Need to put the node back since the ofdma->of_dma_route_allocate | ||
85 | * has taken it for generating the new, translated dma_spec | ||
86 | */ | ||
87 | of_node_put(dma_spec_target.np); | ||
88 | return chan; | ||
89 | } | ||
90 | |||
91 | /** | ||
48 | * of_dma_controller_register - Register a DMA controller to DT DMA helpers | 92 | * of_dma_controller_register - Register a DMA controller to DT DMA helpers |
49 | * @np: device node of DMA controller | 93 | * @np: device node of DMA controller |
50 | * @of_dma_xlate: translation function which converts a phandle | 94 | * @of_dma_xlate: translation function which converts a phandle |
@@ -110,6 +154,51 @@ void of_dma_controller_free(struct device_node *np) | |||
110 | EXPORT_SYMBOL_GPL(of_dma_controller_free); | 154 | EXPORT_SYMBOL_GPL(of_dma_controller_free); |
111 | 155 | ||
112 | /** | 156 | /** |
157 | * of_dma_router_register - Register a DMA router to DT DMA helpers as a | ||
158 | * controller | ||
159 | * @np: device node of DMA router | ||
160 | * @of_dma_route_allocate: setup function for the router which need to | ||
161 | * modify the dma_spec for the DMA controller to | ||
162 | * use and to set up the requested route. | ||
163 | * @dma_router: pointer to dma_router structure to be used when | ||
164 | * the route need to be free up. | ||
165 | * | ||
166 | * Returns 0 on success or appropriate errno value on error. | ||
167 | * | ||
168 | * Allocated memory should be freed with appropriate of_dma_controller_free() | ||
169 | * call. | ||
170 | */ | ||
171 | int of_dma_router_register(struct device_node *np, | ||
172 | void *(*of_dma_route_allocate) | ||
173 | (struct of_phandle_args *, struct of_dma *), | ||
174 | struct dma_router *dma_router) | ||
175 | { | ||
176 | struct of_dma *ofdma; | ||
177 | |||
178 | if (!np || !of_dma_route_allocate || !dma_router) { | ||
179 | pr_err("%s: not enough information provided\n", __func__); | ||
180 | return -EINVAL; | ||
181 | } | ||
182 | |||
183 | ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL); | ||
184 | if (!ofdma) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | ofdma->of_node = np; | ||
188 | ofdma->of_dma_xlate = of_dma_router_xlate; | ||
189 | ofdma->of_dma_route_allocate = of_dma_route_allocate; | ||
190 | ofdma->dma_router = dma_router; | ||
191 | |||
192 | /* Now queue of_dma controller structure in list */ | ||
193 | mutex_lock(&of_dma_lock); | ||
194 | list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); | ||
195 | mutex_unlock(&of_dma_lock); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | EXPORT_SYMBOL_GPL(of_dma_router_register); | ||
200 | |||
201 | /** | ||
113 | * of_dma_match_channel - Check if a DMA specifier matches name | 202 | * of_dma_match_channel - Check if a DMA specifier matches name |
114 | * @np: device node to look for DMA channels | 203 | * @np: device node to look for DMA channels |
115 | * @name: channel name to be matched | 204 | * @name: channel name to be matched |
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 7624a0873412..249445c8a4c6 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c | |||
@@ -22,6 +22,9 @@ | |||
22 | 22 | ||
23 | #include "virt-dma.h" | 23 | #include "virt-dma.h" |
24 | 24 | ||
25 | #define OMAP_SDMA_REQUESTS 127 | ||
26 | #define OMAP_SDMA_CHANNELS 32 | ||
27 | |||
25 | struct omap_dmadev { | 28 | struct omap_dmadev { |
26 | struct dma_device ddev; | 29 | struct dma_device ddev; |
27 | spinlock_t lock; | 30 | spinlock_t lock; |
@@ -31,9 +34,10 @@ struct omap_dmadev { | |||
31 | const struct omap_dma_reg *reg_map; | 34 | const struct omap_dma_reg *reg_map; |
32 | struct omap_system_dma_plat_info *plat; | 35 | struct omap_system_dma_plat_info *plat; |
33 | bool legacy; | 36 | bool legacy; |
37 | unsigned dma_requests; | ||
34 | spinlock_t irq_lock; | 38 | spinlock_t irq_lock; |
35 | uint32_t irq_enable_mask; | 39 | uint32_t irq_enable_mask; |
36 | struct omap_chan *lch_map[32]; | 40 | struct omap_chan *lch_map[OMAP_SDMA_CHANNELS]; |
37 | }; | 41 | }; |
38 | 42 | ||
39 | struct omap_chan { | 43 | struct omap_chan { |
@@ -589,6 +593,7 @@ static void omap_dma_free_chan_resources(struct dma_chan *chan) | |||
589 | omap_free_dma(c->dma_ch); | 593 | omap_free_dma(c->dma_ch); |
590 | 594 | ||
591 | dev_dbg(od->ddev.dev, "freeing channel for %u\n", c->dma_sig); | 595 | dev_dbg(od->ddev.dev, "freeing channel for %u\n", c->dma_sig); |
596 | c->dma_sig = 0; | ||
592 | } | 597 | } |
593 | 598 | ||
594 | static size_t omap_dma_sg_size(struct omap_sg *sg) | 599 | static size_t omap_dma_sg_size(struct omap_sg *sg) |
@@ -1082,7 +1087,7 @@ static int omap_dma_resume(struct dma_chan *chan) | |||
1082 | return 0; | 1087 | return 0; |
1083 | } | 1088 | } |
1084 | 1089 | ||
1085 | static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig) | 1090 | static int omap_dma_chan_init(struct omap_dmadev *od) |
1086 | { | 1091 | { |
1087 | struct omap_chan *c; | 1092 | struct omap_chan *c; |
1088 | 1093 | ||
@@ -1091,7 +1096,6 @@ static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig) | |||
1091 | return -ENOMEM; | 1096 | return -ENOMEM; |
1092 | 1097 | ||
1093 | c->reg_map = od->reg_map; | 1098 | c->reg_map = od->reg_map; |
1094 | c->dma_sig = dma_sig; | ||
1095 | c->vc.desc_free = omap_dma_desc_free; | 1099 | c->vc.desc_free = omap_dma_desc_free; |
1096 | vchan_init(&c->vc, &od->ddev); | 1100 | vchan_init(&c->vc, &od->ddev); |
1097 | INIT_LIST_HEAD(&c->node); | 1101 | INIT_LIST_HEAD(&c->node); |
@@ -1163,8 +1167,17 @@ static int omap_dma_probe(struct platform_device *pdev) | |||
1163 | 1167 | ||
1164 | tasklet_init(&od->task, omap_dma_sched, (unsigned long)od); | 1168 | tasklet_init(&od->task, omap_dma_sched, (unsigned long)od); |
1165 | 1169 | ||
1166 | for (i = 0; i < 127; i++) { | 1170 | od->dma_requests = OMAP_SDMA_REQUESTS; |
1167 | rc = omap_dma_chan_init(od, i); | 1171 | if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node, |
1172 | "dma-requests", | ||
1173 | &od->dma_requests)) { | ||
1174 | dev_info(&pdev->dev, | ||
1175 | "Missing dma-requests property, using %u.\n", | ||
1176 | OMAP_SDMA_REQUESTS); | ||
1177 | } | ||
1178 | |||
1179 | for (i = 0; i < OMAP_SDMA_CHANNELS; i++) { | ||
1180 | rc = omap_dma_chan_init(od); | ||
1168 | if (rc) { | 1181 | if (rc) { |
1169 | omap_dma_free(od); | 1182 | omap_dma_free(od); |
1170 | return rc; | 1183 | return rc; |
@@ -1255,10 +1268,14 @@ static struct platform_driver omap_dma_driver = { | |||
1255 | bool omap_dma_filter_fn(struct dma_chan *chan, void *param) | 1268 | bool omap_dma_filter_fn(struct dma_chan *chan, void *param) |
1256 | { | 1269 | { |
1257 | if (chan->device->dev->driver == &omap_dma_driver.driver) { | 1270 | if (chan->device->dev->driver == &omap_dma_driver.driver) { |
1271 | struct omap_dmadev *od = to_omap_dma_dev(chan->device); | ||
1258 | struct omap_chan *c = to_omap_dma_chan(chan); | 1272 | struct omap_chan *c = to_omap_dma_chan(chan); |
1259 | unsigned req = *(unsigned *)param; | 1273 | unsigned req = *(unsigned *)param; |
1260 | 1274 | ||
1261 | return req == c->dma_sig; | 1275 | if (req <= od->dma_requests) { |
1276 | c->dma_sig = req; | ||
1277 | return true; | ||
1278 | } | ||
1262 | } | 1279 | } |
1263 | return false; | 1280 | return false; |
1264 | } | 1281 | } |
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); | ||
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 5d99229c2f95..2882a201c1cb 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h | |||
@@ -231,6 +231,16 @@ struct dma_chan_percpu { | |||
231 | }; | 231 | }; |
232 | 232 | ||
233 | /** | 233 | /** |
234 | * struct dma_router - DMA router structure | ||
235 | * @dev: pointer to the DMA router device | ||
236 | * @route_free: function to be called when the route can be disconnected | ||
237 | */ | ||
238 | struct dma_router { | ||
239 | struct device *dev; | ||
240 | void (*route_free)(struct device *dev, void *route_data); | ||
241 | }; | ||
242 | |||
243 | /** | ||
234 | * struct dma_chan - devices supply DMA channels, clients use them | 244 | * struct dma_chan - devices supply DMA channels, clients use them |
235 | * @device: ptr to the dma device who supplies this channel, always !%NULL | 245 | * @device: ptr to the dma device who supplies this channel, always !%NULL |
236 | * @cookie: last cookie value returned to client | 246 | * @cookie: last cookie value returned to client |
@@ -241,6 +251,8 @@ struct dma_chan_percpu { | |||
241 | * @local: per-cpu pointer to a struct dma_chan_percpu | 251 | * @local: per-cpu pointer to a struct dma_chan_percpu |
242 | * @client_count: how many clients are using this channel | 252 | * @client_count: how many clients are using this channel |
243 | * @table_count: number of appearances in the mem-to-mem allocation table | 253 | * @table_count: number of appearances in the mem-to-mem allocation table |
254 | * @router: pointer to the DMA router structure | ||
255 | * @route_data: channel specific data for the router | ||
244 | * @private: private data for certain client-channel associations | 256 | * @private: private data for certain client-channel associations |
245 | */ | 257 | */ |
246 | struct dma_chan { | 258 | struct dma_chan { |
@@ -256,6 +268,11 @@ struct dma_chan { | |||
256 | struct dma_chan_percpu __percpu *local; | 268 | struct dma_chan_percpu __percpu *local; |
257 | int client_count; | 269 | int client_count; |
258 | int table_count; | 270 | int table_count; |
271 | |||
272 | /* DMA router */ | ||
273 | struct dma_router *router; | ||
274 | void *route_data; | ||
275 | |||
259 | void *private; | 276 | void *private; |
260 | }; | 277 | }; |
261 | 278 | ||
diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 56bc026c143f..98ba7525929e 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h | |||
@@ -23,6 +23,9 @@ struct of_dma { | |||
23 | struct device_node *of_node; | 23 | struct device_node *of_node; |
24 | struct dma_chan *(*of_dma_xlate) | 24 | struct dma_chan *(*of_dma_xlate) |
25 | (struct of_phandle_args *, struct of_dma *); | 25 | (struct of_phandle_args *, struct of_dma *); |
26 | void *(*of_dma_route_allocate) | ||
27 | (struct of_phandle_args *, struct of_dma *); | ||
28 | struct dma_router *dma_router; | ||
26 | void *of_dma_data; | 29 | void *of_dma_data; |
27 | }; | 30 | }; |
28 | 31 | ||
@@ -37,12 +40,20 @@ extern int of_dma_controller_register(struct device_node *np, | |||
37 | (struct of_phandle_args *, struct of_dma *), | 40 | (struct of_phandle_args *, struct of_dma *), |
38 | void *data); | 41 | void *data); |
39 | extern void of_dma_controller_free(struct device_node *np); | 42 | extern void of_dma_controller_free(struct device_node *np); |
43 | |||
44 | extern int of_dma_router_register(struct device_node *np, | ||
45 | void *(*of_dma_route_allocate) | ||
46 | (struct of_phandle_args *, struct of_dma *), | ||
47 | struct dma_router *dma_router); | ||
48 | #define of_dma_router_free of_dma_controller_free | ||
49 | |||
40 | extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, | 50 | extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, |
41 | const char *name); | 51 | const char *name); |
42 | extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, | 52 | extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, |
43 | struct of_dma *ofdma); | 53 | struct of_dma *ofdma); |
44 | extern struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec, | 54 | extern struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec, |
45 | struct of_dma *ofdma); | 55 | struct of_dma *ofdma); |
56 | |||
46 | #else | 57 | #else |
47 | static inline int of_dma_controller_register(struct device_node *np, | 58 | static inline int of_dma_controller_register(struct device_node *np, |
48 | struct dma_chan *(*of_dma_xlate) | 59 | struct dma_chan *(*of_dma_xlate) |
@@ -56,6 +67,16 @@ static inline void of_dma_controller_free(struct device_node *np) | |||
56 | { | 67 | { |
57 | } | 68 | } |
58 | 69 | ||
70 | static inline int of_dma_router_register(struct device_node *np, | ||
71 | void *(*of_dma_route_allocate) | ||
72 | (struct of_phandle_args *, struct of_dma *), | ||
73 | struct dma_router *dma_router) | ||
74 | { | ||
75 | return -ENODEV; | ||
76 | } | ||
77 | |||
78 | #define of_dma_router_free of_dma_controller_free | ||
79 | |||
59 | static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np, | 80 | static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np, |
60 | const char *name) | 81 | const char *name) |
61 | { | 82 | { |