Skip to content

Commit bbe89c8

Browse files
ldesrochesVinod Koul
authored andcommitted
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 <[email protected]> Acked-by: Nicolas Ferre <[email protected]> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <[email protected]> Acked-by: Arnd Bergmann <[email protected]> Signed-off-by: Vinod Koul <[email protected]>
1 parent e6a30fe commit bbe89c8

File tree

3 files changed

+121
-11
lines changed

3 files changed

+121
-11
lines changed
Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
* Atmel Direct Memory Access Controller (DMA)
22

33
Required properties:
4-
- compatible: Should be "atmel,<chip>-dma"
5-
- reg: Should contain DMA registers location and length
6-
- interrupts: Should contain DMA interrupt
4+
- compatible: Should be "atmel,<chip>-dma".
5+
- reg: Should contain DMA registers location and length.
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.
79

8-
Examples:
10+
Example:
911

10-
dma@ffffec00 {
12+
dma0: dma@ffffec00 {
1113
compatible = "atmel,at91sam9g45-dma";
1214
reg = <0xffffec00 0x200>;
1315
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";
1439
};

drivers/dma/at_hdmac.c

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/slab.h>
2525
#include <linux/of.h>
2626
#include <linux/of_device.h>
27+
#include <linux/of_dma.h>
2728

2829
#include "at_hdmac_regs.h"
2930
#include "dmaengine.h"
@@ -676,7 +677,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
676677
ctrlb |= ATC_DST_ADDR_MODE_FIXED
677678
| ATC_SRC_ADDR_MODE_INCR
678679
| 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);
680681
reg = sconfig->dst_addr;
681682
for_each_sg(sgl, sg, sg_len, i) {
682683
struct at_desc *desc;
@@ -715,7 +716,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
715716
ctrlb |= ATC_DST_ADDR_MODE_INCR
716717
| ATC_SRC_ADDR_MODE_FIXED
717718
| 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);
719720

720721
reg = sconfig->src_addr;
721722
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,
821822
desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED
822823
| ATC_SRC_ADDR_MODE_INCR
823824
| ATC_FC_MEM2PER
824-
| ATC_SIF(AT_DMA_MEM_IF)
825-
| ATC_DIF(AT_DMA_PER_IF);
825+
| ATC_SIF(atchan->mem_if)
826+
| ATC_DIF(atchan->per_if);
826827
break;
827828

828829
case DMA_DEV_TO_MEM:
@@ -832,8 +833,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
832833
desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR
833834
| ATC_SRC_ADDR_MODE_FIXED
834835
| ATC_FC_PER2MEM
835-
| ATC_SIF(AT_DMA_PER_IF)
836-
| ATC_DIF(AT_DMA_MEM_IF);
836+
| ATC_SIF(atchan->per_if)
837+
| ATC_DIF(atchan->mem_if);
837838
break;
838839

839840
default:
@@ -1189,6 +1190,67 @@ static void atc_free_chan_resources(struct dma_chan *chan)
11891190
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
11901191
}
11911192

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
11921254

11931255
/*-- Module Management -----------------------------------------------*/
11941256

@@ -1343,6 +1405,8 @@ static int __init at_dma_probe(struct platform_device *pdev)
13431405
for (i = 0; i < plat_dat->nr_channels; i++) {
13441406
struct at_dma_chan *atchan = &atdma->chan[i];
13451407

1408+
atchan->mem_if = AT_DMA_MEM_IF;
1409+
atchan->per_if = AT_DMA_PER_IF;
13461410
atchan->chan_common.device = &atdma->dma_common;
13471411
dma_cookie_init(&atchan->chan_common);
13481412
list_add_tail(&atchan->chan_common.device_node,
@@ -1389,8 +1453,25 @@ static int __init at_dma_probe(struct platform_device *pdev)
13891453

13901454
dma_async_device_register(&atdma->dma_common);
13911455

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+
13921470
return 0;
13931471

1472+
err_of_dma_controller_register:
1473+
dma_async_device_unregister(&atdma->dma_common);
1474+
dma_pool_destroy(atdma->dma_desc_pool);
13941475
err_pool_create:
13951476
platform_set_drvdata(pdev, NULL);
13961477
free_irq(platform_get_irq(pdev, 0), atdma);

drivers/dma/at_hdmac_regs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ enum atc_status {
220220
* @device: parent device
221221
* @ch_regs: memory mapped register base
222222
* @mask: channel index in a mask
223+
* @per_if: peripheral interface
224+
* @mem_if: memory interface
223225
* @status: transmit status information from irq/prep* functions
224226
* to tasklet (use atomic operations)
225227
* @tasklet: bottom half to finish transaction work
@@ -238,6 +240,8 @@ struct at_dma_chan {
238240
struct at_dma *device;
239241
void __iomem *ch_regs;
240242
u8 mask;
243+
u8 per_if;
244+
u8 mem_if;
241245
unsigned long status;
242246
struct tasklet_struct tasklet;
243247
u32 save_cfg;

0 commit comments

Comments
 (0)