diff options
author | Ludovic Desroches <ludovic.desroches@atmel.com> | 2013-04-19 05:11:18 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2013-04-30 04:47:41 -0400 |
commit | bbe89c8e3d598129b728d1388c3ad9abe4e8e261 (patch) | |
tree | b4654e214de37c86501ad83005c29a8419a97a3d | |
parent | e6a30fec08b421a59064437a7d990c70a80a7e7f (diff) |
at_hdmac: move to generic DMA binding
Update at_hdmac driver to support generic DMA device tree binding. Devices
can still request channel with dma_request_channel() then it doesn't break
DMA for non DT boards.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r-- | Documentation/devicetree/bindings/dma/atmel-dma.txt | 35 | ||||
-rw-r--r-- | drivers/dma/at_hdmac.c | 93 | ||||
-rw-r--r-- | drivers/dma/at_hdmac_regs.h | 4 |
3 files changed, 121 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/dma/atmel-dma.txt b/Documentation/devicetree/bindings/dma/atmel-dma.txt index 3c046ee6e8b5..c80e8a3402f0 100644 --- a/Documentation/devicetree/bindings/dma/atmel-dma.txt +++ b/Documentation/devicetree/bindings/dma/atmel-dma.txt | |||
@@ -1,14 +1,39 @@ | |||
1 | * Atmel Direct Memory Access Controller (DMA) | 1 | * Atmel Direct Memory Access Controller (DMA) |
2 | 2 | ||
3 | Required properties: | 3 | Required properties: |
4 | - compatible: Should be "atmel,<chip>-dma" | 4 | - compatible: Should be "atmel,<chip>-dma". |
5 | - reg: Should contain DMA registers location and length | 5 | - reg: Should contain DMA registers location and length. |
6 | - interrupts: Should contain DMA interrupt | 6 | - interrupts: Should contain DMA interrupt. |
7 | - #dma-cells: Must be <2>, used to represent the number of integer cells in | ||
8 | the dmas property of client devices. | ||
7 | 9 | ||
8 | Examples: | 10 | Example: |
9 | 11 | ||
10 | dma@ffffec00 { | 12 | dma0: dma@ffffec00 { |
11 | compatible = "atmel,at91sam9g45-dma"; | 13 | compatible = "atmel,at91sam9g45-dma"; |
12 | reg = <0xffffec00 0x200>; | 14 | reg = <0xffffec00 0x200>; |
13 | interrupts = <21>; | 15 | interrupts = <21>; |
16 | #dma-cells = <2>; | ||
17 | }; | ||
18 | |||
19 | DMA clients connected to the Atmel DMA controller must use the format | ||
20 | described in the dma.txt file, using a three-cell specifier for each channel: | ||
21 | a phandle plus two interger cells. | ||
22 | The three cells in order are: | ||
23 | |||
24 | 1. A phandle pointing to the DMA controller. | ||
25 | 2. The memory interface (16 most significant bits), the peripheral interface | ||
26 | (16 less significant bits). | ||
27 | 3. The peripheral identifier for the hardware handshaking interface. The | ||
28 | identifier can be different for tx and rx. | ||
29 | |||
30 | Example: | ||
31 | |||
32 | i2c0@i2c@f8010000 { | ||
33 | compatible = "atmel,at91sam9x5-i2c"; | ||
34 | reg = <0xf8010000 0x100>; | ||
35 | interrupts = <9 4 6>; | ||
36 | dmas = <&dma0 1 7>, | ||
37 | <&dma0 1 8>; | ||
38 | dma-names = "tx", "rx"; | ||
14 | }; | 39 | }; |
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 841546776159..3502c412caf9 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/of.h> | 25 | #include <linux/of.h> |
26 | #include <linux/of_device.h> | 26 | #include <linux/of_device.h> |
27 | #include <linux/of_dma.h> | ||
27 | 28 | ||
28 | #include "at_hdmac_regs.h" | 29 | #include "at_hdmac_regs.h" |
29 | #include "dmaengine.h" | 30 | #include "dmaengine.h" |
@@ -676,7 +677,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
676 | ctrlb |= ATC_DST_ADDR_MODE_FIXED | 677 | ctrlb |= ATC_DST_ADDR_MODE_FIXED |
677 | | ATC_SRC_ADDR_MODE_INCR | 678 | | ATC_SRC_ADDR_MODE_INCR |
678 | | ATC_FC_MEM2PER | 679 | | ATC_FC_MEM2PER |
679 | | ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF); | 680 | | ATC_SIF(atchan->mem_if) | ATC_DIF(atchan->per_if); |
680 | reg = sconfig->dst_addr; | 681 | reg = sconfig->dst_addr; |
681 | for_each_sg(sgl, sg, sg_len, i) { | 682 | for_each_sg(sgl, sg, sg_len, i) { |
682 | struct at_desc *desc; | 683 | struct at_desc *desc; |
@@ -715,7 +716,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
715 | ctrlb |= ATC_DST_ADDR_MODE_INCR | 716 | ctrlb |= ATC_DST_ADDR_MODE_INCR |
716 | | ATC_SRC_ADDR_MODE_FIXED | 717 | | ATC_SRC_ADDR_MODE_FIXED |
717 | | ATC_FC_PER2MEM | 718 | | ATC_FC_PER2MEM |
718 | | ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF); | 719 | | ATC_SIF(atchan->per_if) | ATC_DIF(atchan->mem_if); |
719 | 720 | ||
720 | reg = sconfig->src_addr; | 721 | reg = sconfig->src_addr; |
721 | for_each_sg(sgl, sg, sg_len, i) { | 722 | for_each_sg(sgl, sg, sg_len, i) { |
@@ -821,8 +822,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, | |||
821 | desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED | 822 | desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED |
822 | | ATC_SRC_ADDR_MODE_INCR | 823 | | ATC_SRC_ADDR_MODE_INCR |
823 | | ATC_FC_MEM2PER | 824 | | ATC_FC_MEM2PER |
824 | | ATC_SIF(AT_DMA_MEM_IF) | 825 | | ATC_SIF(atchan->mem_if) |
825 | | ATC_DIF(AT_DMA_PER_IF); | 826 | | ATC_DIF(atchan->per_if); |
826 | break; | 827 | break; |
827 | 828 | ||
828 | case DMA_DEV_TO_MEM: | 829 | case DMA_DEV_TO_MEM: |
@@ -832,8 +833,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, | |||
832 | desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR | 833 | desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR |
833 | | ATC_SRC_ADDR_MODE_FIXED | 834 | | ATC_SRC_ADDR_MODE_FIXED |
834 | | ATC_FC_PER2MEM | 835 | | ATC_FC_PER2MEM |
835 | | ATC_SIF(AT_DMA_PER_IF) | 836 | | ATC_SIF(atchan->per_if) |
836 | | ATC_DIF(AT_DMA_MEM_IF); | 837 | | ATC_DIF(atchan->mem_if); |
837 | break; | 838 | break; |
838 | 839 | ||
839 | default: | 840 | default: |
@@ -1189,6 +1190,67 @@ static void atc_free_chan_resources(struct dma_chan *chan) | |||
1189 | dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); | 1190 | dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); |
1190 | } | 1191 | } |
1191 | 1192 | ||
1193 | #ifdef CONFIG_OF | ||
1194 | static bool at_dma_filter(struct dma_chan *chan, void *slave) | ||
1195 | { | ||
1196 | struct at_dma_slave *atslave = slave; | ||
1197 | |||
1198 | if (atslave->dma_dev == chan->device->dev) { | ||
1199 | chan->private = atslave; | ||
1200 | return true; | ||
1201 | } else { | ||
1202 | return false; | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1206 | static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec, | ||
1207 | struct of_dma *of_dma) | ||
1208 | { | ||
1209 | struct dma_chan *chan; | ||
1210 | struct at_dma_chan *atchan; | ||
1211 | struct at_dma_slave *atslave; | ||
1212 | dma_cap_mask_t mask; | ||
1213 | unsigned int per_id; | ||
1214 | struct platform_device *dmac_pdev; | ||
1215 | |||
1216 | if (dma_spec->args_count != 2) | ||
1217 | return NULL; | ||
1218 | |||
1219 | dmac_pdev = of_find_device_by_node(dma_spec->np); | ||
1220 | |||
1221 | dma_cap_zero(mask); | ||
1222 | dma_cap_set(DMA_SLAVE, mask); | ||
1223 | |||
1224 | atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL); | ||
1225 | if (!atslave) | ||
1226 | return NULL; | ||
1227 | /* | ||
1228 | * We can fill both SRC_PER and DST_PER, one of these fields will be | ||
1229 | * ignored depending on DMA transfer direction. | ||
1230 | */ | ||
1231 | per_id = dma_spec->args[1]; | ||
1232 | atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW | ||
1233 | | ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id) | ||
1234 | | ATC_SRC_PER(per_id); | ||
1235 | atslave->dma_dev = &dmac_pdev->dev; | ||
1236 | |||
1237 | chan = dma_request_channel(mask, at_dma_filter, atslave); | ||
1238 | if (!chan) | ||
1239 | return NULL; | ||
1240 | |||
1241 | atchan = to_at_dma_chan(chan); | ||
1242 | atchan->per_if = dma_spec->args[0] & 0xff; | ||
1243 | atchan->mem_if = (dma_spec->args[0] >> 16) & 0xff; | ||
1244 | |||
1245 | return chan; | ||
1246 | } | ||
1247 | #else | ||
1248 | static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec, | ||
1249 | struct of_dma *of_dma) | ||
1250 | { | ||
1251 | return NULL; | ||
1252 | } | ||
1253 | #endif | ||
1192 | 1254 | ||
1193 | /*-- Module Management -----------------------------------------------*/ | 1255 | /*-- Module Management -----------------------------------------------*/ |
1194 | 1256 | ||
@@ -1343,6 +1405,8 @@ static int __init at_dma_probe(struct platform_device *pdev) | |||
1343 | for (i = 0; i < plat_dat->nr_channels; i++) { | 1405 | for (i = 0; i < plat_dat->nr_channels; i++) { |
1344 | struct at_dma_chan *atchan = &atdma->chan[i]; | 1406 | struct at_dma_chan *atchan = &atdma->chan[i]; |
1345 | 1407 | ||
1408 | atchan->mem_if = AT_DMA_MEM_IF; | ||
1409 | atchan->per_if = AT_DMA_PER_IF; | ||
1346 | atchan->chan_common.device = &atdma->dma_common; | 1410 | atchan->chan_common.device = &atdma->dma_common; |
1347 | dma_cookie_init(&atchan->chan_common); | 1411 | dma_cookie_init(&atchan->chan_common); |
1348 | list_add_tail(&atchan->chan_common.device_node, | 1412 | list_add_tail(&atchan->chan_common.device_node, |
@@ -1389,8 +1453,25 @@ static int __init at_dma_probe(struct platform_device *pdev) | |||
1389 | 1453 | ||
1390 | dma_async_device_register(&atdma->dma_common); | 1454 | dma_async_device_register(&atdma->dma_common); |
1391 | 1455 | ||
1456 | /* | ||
1457 | * Do not return an error if the dmac node is not present in order to | ||
1458 | * not break the existing way of requesting channel with | ||
1459 | * dma_request_channel(). | ||
1460 | */ | ||
1461 | if (pdev->dev.of_node) { | ||
1462 | err = of_dma_controller_register(pdev->dev.of_node, | ||
1463 | at_dma_xlate, atdma); | ||
1464 | if (err) { | ||
1465 | dev_err(&pdev->dev, "could not register of_dma_controller\n"); | ||
1466 | goto err_of_dma_controller_register; | ||
1467 | } | ||
1468 | } | ||
1469 | |||
1392 | return 0; | 1470 | return 0; |
1393 | 1471 | ||
1472 | err_of_dma_controller_register: | ||
1473 | dma_async_device_unregister(&atdma->dma_common); | ||
1474 | dma_pool_destroy(atdma->dma_desc_pool); | ||
1394 | err_pool_create: | 1475 | err_pool_create: |
1395 | platform_set_drvdata(pdev, NULL); | 1476 | platform_set_drvdata(pdev, NULL); |
1396 | free_irq(platform_get_irq(pdev, 0), atdma); | 1477 | free_irq(platform_get_irq(pdev, 0), atdma); |
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 0eb3c1388667..c604d26fd4d3 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h | |||
@@ -220,6 +220,8 @@ enum atc_status { | |||
220 | * @device: parent device | 220 | * @device: parent device |
221 | * @ch_regs: memory mapped register base | 221 | * @ch_regs: memory mapped register base |
222 | * @mask: channel index in a mask | 222 | * @mask: channel index in a mask |
223 | * @per_if: peripheral interface | ||
224 | * @mem_if: memory interface | ||
223 | * @status: transmit status information from irq/prep* functions | 225 | * @status: transmit status information from irq/prep* functions |
224 | * to tasklet (use atomic operations) | 226 | * to tasklet (use atomic operations) |
225 | * @tasklet: bottom half to finish transaction work | 227 | * @tasklet: bottom half to finish transaction work |
@@ -238,6 +240,8 @@ struct at_dma_chan { | |||
238 | struct at_dma *device; | 240 | struct at_dma *device; |
239 | void __iomem *ch_regs; | 241 | void __iomem *ch_regs; |
240 | u8 mask; | 242 | u8 mask; |
243 | u8 per_if; | ||
244 | u8 mem_if; | ||
241 | unsigned long status; | 245 | unsigned long status; |
242 | struct tasklet_struct tasklet; | 246 | struct tasklet_struct tasklet; |
243 | u32 save_cfg; | 247 | u32 save_cfg; |