Commit bbe89c8e authored by Ludovic Desroches's avatar Ludovic Desroches Committed by Vinod Koul

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: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Acked-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: default avatarJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent e6a30fec
* Atmel Direct Memory Access Controller (DMA) * Atmel Direct Memory Access Controller (DMA)
Required properties: Required properties:
- compatible: Should be "atmel,<chip>-dma" - compatible: Should be "atmel,<chip>-dma".
- reg: Should contain DMA registers location and length - reg: Should contain DMA registers location and length.
- interrupts: Should contain DMA interrupt - interrupts: Should contain DMA interrupt.
- #dma-cells: Must be <2>, used to represent the number of integer cells in
the dmas property of client devices.
Examples: Example:
dma@ffffec00 { dma0: dma@ffffec00 {
compatible = "atmel,at91sam9g45-dma"; compatible = "atmel,at91sam9g45-dma";
reg = <0xffffec00 0x200>; reg = <0xffffec00 0x200>;
interrupts = <21>; interrupts = <21>;
#dma-cells = <2>;
};
DMA clients connected to the Atmel DMA controller must use the format
described in the dma.txt file, using a three-cell specifier for each channel:
a phandle plus two interger cells.
The three cells in order are:
1. A phandle pointing to the DMA controller.
2. The memory interface (16 most significant bits), the peripheral interface
(16 less significant bits).
3. The peripheral identifier for the hardware handshaking interface. The
identifier can be different for tx and rx.
Example:
i2c0@i2c@f8010000 {
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8010000 0x100>;
interrupts = <9 4 6>;
dmas = <&dma0 1 7>,
<&dma0 1 8>;
dma-names = "tx", "rx";
}; };
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_dma.h>
#include "at_hdmac_regs.h" #include "at_hdmac_regs.h"
#include "dmaengine.h" #include "dmaengine.h"
...@@ -676,7 +677,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -676,7 +677,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
ctrlb |= ATC_DST_ADDR_MODE_FIXED ctrlb |= ATC_DST_ADDR_MODE_FIXED
| ATC_SRC_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_INCR
| ATC_FC_MEM2PER | ATC_FC_MEM2PER
| ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF); | ATC_SIF(atchan->mem_if) | ATC_DIF(atchan->per_if);
reg = sconfig->dst_addr; reg = sconfig->dst_addr;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
struct at_desc *desc; struct at_desc *desc;
...@@ -715,7 +716,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -715,7 +716,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
ctrlb |= ATC_DST_ADDR_MODE_INCR ctrlb |= ATC_DST_ADDR_MODE_INCR
| ATC_SRC_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_FIXED
| ATC_FC_PER2MEM | ATC_FC_PER2MEM
| ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF); | ATC_SIF(atchan->per_if) | ATC_DIF(atchan->mem_if);
reg = sconfig->src_addr; reg = sconfig->src_addr;
for_each_sg(sgl, sg, sg_len, i) { 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,8 +822,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED
| ATC_SRC_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_INCR
| ATC_FC_MEM2PER | ATC_FC_MEM2PER
| ATC_SIF(AT_DMA_MEM_IF) | ATC_SIF(atchan->mem_if)
| ATC_DIF(AT_DMA_PER_IF); | ATC_DIF(atchan->per_if);
break; break;
case DMA_DEV_TO_MEM: case DMA_DEV_TO_MEM:
...@@ -832,8 +833,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, ...@@ -832,8 +833,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR
| ATC_SRC_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_FIXED
| ATC_FC_PER2MEM | ATC_FC_PER2MEM
| ATC_SIF(AT_DMA_PER_IF) | ATC_SIF(atchan->per_if)
| ATC_DIF(AT_DMA_MEM_IF); | ATC_DIF(atchan->mem_if);
break; break;
default: default:
...@@ -1189,6 +1190,67 @@ static void atc_free_chan_resources(struct dma_chan *chan) ...@@ -1189,6 +1190,67 @@ static void atc_free_chan_resources(struct dma_chan *chan)
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
} }
#ifdef CONFIG_OF
static bool at_dma_filter(struct dma_chan *chan, void *slave)
{
struct at_dma_slave *atslave = slave;
if (atslave->dma_dev == chan->device->dev) {
chan->private = atslave;
return true;
} else {
return false;
}
}
static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
struct of_dma *of_dma)
{
struct dma_chan *chan;
struct at_dma_chan *atchan;
struct at_dma_slave *atslave;
dma_cap_mask_t mask;
unsigned int per_id;
struct platform_device *dmac_pdev;
if (dma_spec->args_count != 2)
return NULL;
dmac_pdev = of_find_device_by_node(dma_spec->np);
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL);
if (!atslave)
return NULL;
/*
* We can fill both SRC_PER and DST_PER, one of these fields will be
* ignored depending on DMA transfer direction.
*/
per_id = dma_spec->args[1];
atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW
| ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id)
| ATC_SRC_PER(per_id);
atslave->dma_dev = &dmac_pdev->dev;
chan = dma_request_channel(mask, at_dma_filter, atslave);
if (!chan)
return NULL;
atchan = to_at_dma_chan(chan);
atchan->per_if = dma_spec->args[0] & 0xff;
atchan->mem_if = (dma_spec->args[0] >> 16) & 0xff;
return chan;
}
#else
static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
struct of_dma *of_dma)
{
return NULL;
}
#endif
/*-- Module Management -----------------------------------------------*/ /*-- Module Management -----------------------------------------------*/
...@@ -1343,6 +1405,8 @@ static int __init at_dma_probe(struct platform_device *pdev) ...@@ -1343,6 +1405,8 @@ static int __init at_dma_probe(struct platform_device *pdev)
for (i = 0; i < plat_dat->nr_channels; i++) { for (i = 0; i < plat_dat->nr_channels; i++) {
struct at_dma_chan *atchan = &atdma->chan[i]; struct at_dma_chan *atchan = &atdma->chan[i];
atchan->mem_if = AT_DMA_MEM_IF;
atchan->per_if = AT_DMA_PER_IF;
atchan->chan_common.device = &atdma->dma_common; atchan->chan_common.device = &atdma->dma_common;
dma_cookie_init(&atchan->chan_common); dma_cookie_init(&atchan->chan_common);
list_add_tail(&atchan->chan_common.device_node, list_add_tail(&atchan->chan_common.device_node,
...@@ -1389,8 +1453,25 @@ static int __init at_dma_probe(struct platform_device *pdev) ...@@ -1389,8 +1453,25 @@ static int __init at_dma_probe(struct platform_device *pdev)
dma_async_device_register(&atdma->dma_common); dma_async_device_register(&atdma->dma_common);
/*
* Do not return an error if the dmac node is not present in order to
* not break the existing way of requesting channel with
* dma_request_channel().
*/
if (pdev->dev.of_node) {
err = of_dma_controller_register(pdev->dev.of_node,
at_dma_xlate, atdma);
if (err) {
dev_err(&pdev->dev, "could not register of_dma_controller\n");
goto err_of_dma_controller_register;
}
}
return 0; return 0;
err_of_dma_controller_register:
dma_async_device_unregister(&atdma->dma_common);
dma_pool_destroy(atdma->dma_desc_pool);
err_pool_create: err_pool_create:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
free_irq(platform_get_irq(pdev, 0), atdma); free_irq(platform_get_irq(pdev, 0), atdma);
......
...@@ -220,6 +220,8 @@ enum atc_status { ...@@ -220,6 +220,8 @@ enum atc_status {
* @device: parent device * @device: parent device
* @ch_regs: memory mapped register base * @ch_regs: memory mapped register base
* @mask: channel index in a mask * @mask: channel index in a mask
* @per_if: peripheral interface
* @mem_if: memory interface
* @status: transmit status information from irq/prep* functions * @status: transmit status information from irq/prep* functions
* to tasklet (use atomic operations) * to tasklet (use atomic operations)
* @tasklet: bottom half to finish transaction work * @tasklet: bottom half to finish transaction work
...@@ -238,6 +240,8 @@ struct at_dma_chan { ...@@ -238,6 +240,8 @@ struct at_dma_chan {
struct at_dma *device; struct at_dma *device;
void __iomem *ch_regs; void __iomem *ch_regs;
u8 mask; u8 mask;
u8 per_if;
u8 mem_if;
unsigned long status; unsigned long status;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
u32 save_cfg; u32 save_cfg;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment