Merge branch 'next' of git://git.infradead.org/users/vkoul/slave-dma
Pull second set of slave-dmaengine updates from Vinod Koul: "Arnd's patch moves the dw_dmac to use generic DMA binding. I agreed to merge this late as it will avoid the conflicts between trees. The second patch from Matt adding a dma_request_slave_channel_compat API was supposed to be picked up, but somehow never got picked up. Some patches dependent on this are already in -next :(" * 'next' of git://git.infradead.org/users/vkoul/slave-dma: dmaengine: dw_dmac: move to generic DMA binding dmaengine: add dma_request_slave_channel_compat()
This commit is contained in:
commit
527c680f7c
|
@ -3,59 +3,61 @@
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "snps,dma-spear1340"
|
- compatible: "snps,dma-spear1340"
|
||||||
- reg: Address range of the DMAC registers
|
- reg: Address range of the DMAC registers
|
||||||
- interrupt-parent: Should be the phandle for the interrupt controller
|
|
||||||
that services interrupts for this device
|
|
||||||
- interrupt: Should contain the DMAC interrupt number
|
- interrupt: Should contain the DMAC interrupt number
|
||||||
- nr_channels: Number of channels supported by hardware
|
- dma-channels: Number of channels supported by hardware
|
||||||
- is_private: The device channels should be marked as private and not for by the
|
- dma-requests: Number of DMA request lines supported, up to 16
|
||||||
general purpose DMA channel allocator. False if not passed.
|
- dma-masters: Number of AHB masters supported by the controller
|
||||||
|
- #dma-cells: must be <3>
|
||||||
- chan_allocation_order: order of allocation of channel, 0 (default): ascending,
|
- chan_allocation_order: order of allocation of channel, 0 (default): ascending,
|
||||||
1: descending
|
1: descending
|
||||||
- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1:
|
- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1:
|
||||||
increase from chan n->0
|
increase from chan n->0
|
||||||
- block_size: Maximum block size supported by the controller
|
- block_size: Maximum block size supported by the controller
|
||||||
- nr_masters: Number of AHB masters supported by the controller
|
|
||||||
- data_width: Maximum data width supported by hardware per AHB master
|
- data_width: Maximum data width supported by hardware per AHB master
|
||||||
(0 - 8bits, 1 - 16bits, ..., 5 - 256bits)
|
(0 - 8bits, 1 - 16bits, ..., 5 - 256bits)
|
||||||
- slave_info:
|
|
||||||
- bus_id: name of this device channel, not just a device name since
|
|
||||||
devices may have more than one channel e.g. "foo_tx". For using the
|
Optional properties:
|
||||||
dw_generic_filter(), slave drivers must pass exactly this string as
|
- interrupt-parent: Should be the phandle for the interrupt controller
|
||||||
param to filter function.
|
that services interrupts for this device
|
||||||
- cfg_hi: Platform-specific initializer for the CFG_HI register
|
- is_private: The device channels should be marked as private and not for by the
|
||||||
- cfg_lo: Platform-specific initializer for the CFG_LO register
|
general purpose DMA channel allocator. False if not passed.
|
||||||
- src_master: src master for transfers on allocated channel.
|
|
||||||
- dst_master: dest master for transfers on allocated channel.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
dma@fc000000 {
|
dmahost: dma@fc000000 {
|
||||||
compatible = "snps,dma-spear1340";
|
compatible = "snps,dma-spear1340";
|
||||||
reg = <0xfc000000 0x1000>;
|
reg = <0xfc000000 0x1000>;
|
||||||
interrupt-parent = <&vic1>;
|
interrupt-parent = <&vic1>;
|
||||||
interrupts = <12>;
|
interrupts = <12>;
|
||||||
|
|
||||||
nr_channels = <8>;
|
dma-channels = <8>;
|
||||||
|
dma-requests = <16>;
|
||||||
|
dma-masters = <2>;
|
||||||
|
#dma-cells = <3>;
|
||||||
chan_allocation_order = <1>;
|
chan_allocation_order = <1>;
|
||||||
chan_priority = <1>;
|
chan_priority = <1>;
|
||||||
block_size = <0xfff>;
|
block_size = <0xfff>;
|
||||||
nr_masters = <2>;
|
|
||||||
data_width = <3 3 0 0>;
|
data_width = <3 3 0 0>;
|
||||||
|
};
|
||||||
slave_info {
|
|
||||||
uart0-tx {
|
DMA clients connected to the Designware DMA controller must use the format
|
||||||
bus_id = "uart0-tx";
|
described in the dma.txt file, using a four-cell specifier for each channel.
|
||||||
cfg_hi = <0x4000>; /* 0x8 << 11 */
|
The four cells in order are:
|
||||||
cfg_lo = <0>;
|
|
||||||
src_master = <0>;
|
1. A phandle pointing to the DMA controller
|
||||||
dst_master = <1>;
|
2. The DMA request line number
|
||||||
};
|
3. Source master for transfers on allocated channel
|
||||||
spi0-tx {
|
4. Destination master for transfers on allocated channel
|
||||||
bus_id = "spi0-tx";
|
|
||||||
cfg_hi = <0x2000>; /* 0x4 << 11 */
|
Example:
|
||||||
cfg_lo = <0>;
|
|
||||||
src_master = <0>;
|
serial@e0000000 {
|
||||||
dst_master = <0>;
|
compatible = "arm,pl011", "arm,primecell";
|
||||||
};
|
reg = <0xe0000000 0x1000>;
|
||||||
};
|
interrupts = <0 35 0x4>;
|
||||||
|
status = "disabled";
|
||||||
|
dmas = <&dmahost 12 0 1>,
|
||||||
|
<&dmahost 13 0 1 0>;
|
||||||
|
dma-names = "rx", "rx";
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_dma.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -171,7 +172,13 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
|
||||||
if (dwc->initialized == true)
|
if (dwc->initialized == true)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (dws) {
|
if (dws && dws->cfg_hi == ~0 && dws->cfg_lo == ~0) {
|
||||||
|
/* autoconfigure based on request line from DT */
|
||||||
|
if (dwc->direction == DMA_MEM_TO_DEV)
|
||||||
|
cfghi = DWC_CFGH_DST_PER(dwc->request_line);
|
||||||
|
else if (dwc->direction == DMA_DEV_TO_MEM)
|
||||||
|
cfghi = DWC_CFGH_SRC_PER(dwc->request_line);
|
||||||
|
} else if (dws) {
|
||||||
/*
|
/*
|
||||||
* We need controller-specific data to set up slave
|
* We need controller-specific data to set up slave
|
||||||
* transfers.
|
* transfers.
|
||||||
|
@ -1226,49 +1233,64 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
|
||||||
dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
|
dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dw_dma_generic_filter(struct dma_chan *chan, void *param)
|
struct dw_dma_filter_args {
|
||||||
|
struct dw_dma *dw;
|
||||||
|
unsigned int req;
|
||||||
|
unsigned int src;
|
||||||
|
unsigned int dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool dw_dma_generic_filter(struct dma_chan *chan, void *param)
|
||||||
{
|
{
|
||||||
|
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||||
struct dw_dma *dw = to_dw_dma(chan->device);
|
struct dw_dma *dw = to_dw_dma(chan->device);
|
||||||
static struct dw_dma *last_dw;
|
struct dw_dma_filter_args *fargs = param;
|
||||||
static char *last_bus_id;
|
struct dw_dma_slave *dws = &dwc->slave;
|
||||||
int i = -1;
|
|
||||||
|
|
||||||
/*
|
/* ensure the device matches our channel */
|
||||||
* dmaengine framework calls this routine for all channels of all dma
|
if (chan->device != &fargs->dw->dma)
|
||||||
* controller, until true is returned. If 'param' bus_id is not
|
return false;
|
||||||
* registered with a dma controller (dw), then there is no need of
|
|
||||||
* running below function for all channels of dw.
|
|
||||||
*
|
|
||||||
* This block of code does this by saving the parameters of last
|
|
||||||
* failure. If dw and param are same, i.e. trying on same dw with
|
|
||||||
* different channel, return false.
|
|
||||||
*/
|
|
||||||
if ((last_dw == dw) && (last_bus_id == param))
|
|
||||||
return false;
|
|
||||||
/*
|
|
||||||
* Return true:
|
|
||||||
* - If dw_dma's platform data is not filled with slave info, then all
|
|
||||||
* dma controllers are fine for transfer.
|
|
||||||
* - Or if param is NULL
|
|
||||||
*/
|
|
||||||
if (!dw->sd || !param)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
while (++i < dw->sd_count) {
|
dws->dma_dev = dw->dma.dev;
|
||||||
if (!strcmp(dw->sd[i].bus_id, param)) {
|
dws->cfg_hi = ~0;
|
||||||
chan->private = &dw->sd[i];
|
dws->cfg_lo = ~0;
|
||||||
last_dw = NULL;
|
dws->src_master = fargs->src;
|
||||||
last_bus_id = NULL;
|
dws->dst_master = fargs->dst;
|
||||||
|
|
||||||
return true;
|
dwc->request_line = fargs->req;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
last_dw = dw;
|
chan->private = dws;
|
||||||
last_bus_id = param;
|
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dma_chan *dw_dma_xlate(struct of_phandle_args *dma_spec,
|
||||||
|
struct of_dma *ofdma)
|
||||||
|
{
|
||||||
|
struct dw_dma *dw = ofdma->of_dma_data;
|
||||||
|
struct dw_dma_filter_args fargs = {
|
||||||
|
.dw = dw,
|
||||||
|
};
|
||||||
|
dma_cap_mask_t cap;
|
||||||
|
|
||||||
|
if (dma_spec->args_count != 3)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fargs.req = be32_to_cpup(dma_spec->args+0);
|
||||||
|
fargs.src = be32_to_cpup(dma_spec->args+1);
|
||||||
|
fargs.dst = be32_to_cpup(dma_spec->args+2);
|
||||||
|
|
||||||
|
if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS ||
|
||||||
|
fargs.src >= dw->nr_masters ||
|
||||||
|
fargs.dst >= dw->nr_masters))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dma_cap_zero(cap);
|
||||||
|
dma_cap_set(DMA_SLAVE, cap);
|
||||||
|
|
||||||
|
/* TODO: there should be a simpler way to do this */
|
||||||
|
return dma_request_channel(cap, dw_dma_generic_filter, &fargs);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dw_dma_generic_filter);
|
|
||||||
|
|
||||||
/* --------------------- Cyclic DMA API extensions -------------------- */
|
/* --------------------- Cyclic DMA API extensions -------------------- */
|
||||||
|
|
||||||
|
@ -1554,9 +1576,8 @@ static void dw_dma_off(struct dw_dma *dw)
|
||||||
static struct dw_dma_platform_data *
|
static struct dw_dma_platform_data *
|
||||||
dw_dma_parse_dt(struct platform_device *pdev)
|
dw_dma_parse_dt(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *sn, *cn, *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
struct dw_dma_platform_data *pdata;
|
struct dw_dma_platform_data *pdata;
|
||||||
struct dw_dma_slave *sd;
|
|
||||||
u32 tmp, arr[4];
|
u32 tmp, arr[4];
|
||||||
|
|
||||||
if (!np) {
|
if (!np) {
|
||||||
|
@ -1568,7 +1589,7 @@ dw_dma_parse_dt(struct platform_device *pdev)
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (of_property_read_u32(np, "nr_channels", &pdata->nr_channels))
|
if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (of_property_read_bool(np, "is_private"))
|
if (of_property_read_bool(np, "is_private"))
|
||||||
|
@ -1583,7 +1604,7 @@ dw_dma_parse_dt(struct platform_device *pdev)
|
||||||
if (!of_property_read_u32(np, "block_size", &tmp))
|
if (!of_property_read_u32(np, "block_size", &tmp))
|
||||||
pdata->block_size = tmp;
|
pdata->block_size = tmp;
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "nr_masters", &tmp)) {
|
if (!of_property_read_u32(np, "dma-masters", &tmp)) {
|
||||||
if (tmp > 4)
|
if (tmp > 4)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1595,36 +1616,6 @@ dw_dma_parse_dt(struct platform_device *pdev)
|
||||||
for (tmp = 0; tmp < pdata->nr_masters; tmp++)
|
for (tmp = 0; tmp < pdata->nr_masters; tmp++)
|
||||||
pdata->data_width[tmp] = arr[tmp];
|
pdata->data_width[tmp] = arr[tmp];
|
||||||
|
|
||||||
/* parse slave data */
|
|
||||||
sn = of_find_node_by_name(np, "slave_info");
|
|
||||||
if (!sn)
|
|
||||||
return pdata;
|
|
||||||
|
|
||||||
/* calculate number of slaves */
|
|
||||||
tmp = of_get_child_count(sn);
|
|
||||||
if (!tmp)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
sd = devm_kzalloc(&pdev->dev, sizeof(*sd) * tmp, GFP_KERNEL);
|
|
||||||
if (!sd)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
pdata->sd = sd;
|
|
||||||
pdata->sd_count = tmp;
|
|
||||||
|
|
||||||
for_each_child_of_node(sn, cn) {
|
|
||||||
sd->dma_dev = &pdev->dev;
|
|
||||||
of_property_read_string(cn, "bus_id", &sd->bus_id);
|
|
||||||
of_property_read_u32(cn, "cfg_hi", &sd->cfg_hi);
|
|
||||||
of_property_read_u32(cn, "cfg_lo", &sd->cfg_lo);
|
|
||||||
if (!of_property_read_u32(cn, "src_master", &tmp))
|
|
||||||
sd->src_master = tmp;
|
|
||||||
|
|
||||||
if (!of_property_read_u32(cn, "dst_master", &tmp))
|
|
||||||
sd->dst_master = tmp;
|
|
||||||
sd++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -1705,8 +1696,6 @@ static int dw_probe(struct platform_device *pdev)
|
||||||
clk_prepare_enable(dw->clk);
|
clk_prepare_enable(dw->clk);
|
||||||
|
|
||||||
dw->regs = regs;
|
dw->regs = regs;
|
||||||
dw->sd = pdata->sd;
|
|
||||||
dw->sd_count = pdata->sd_count;
|
|
||||||
|
|
||||||
/* get hardware configuration parameters */
|
/* get hardware configuration parameters */
|
||||||
if (autocfg) {
|
if (autocfg) {
|
||||||
|
@ -1837,6 +1826,14 @@ static int dw_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dma_async_device_register(&dw->dma);
|
dma_async_device_register(&dw->dma);
|
||||||
|
|
||||||
|
if (pdev->dev.of_node) {
|
||||||
|
err = of_dma_controller_register(pdev->dev.of_node,
|
||||||
|
dw_dma_xlate, dw);
|
||||||
|
if (err && err != -ENODEV)
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"could not register of_dma_controller\n");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1845,6 +1842,8 @@ static int dw_remove(struct platform_device *pdev)
|
||||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
struct dw_dma *dw = platform_get_drvdata(pdev);
|
||||||
struct dw_dma_chan *dwc, *_dwc;
|
struct dw_dma_chan *dwc, *_dwc;
|
||||||
|
|
||||||
|
if (pdev->dev.of_node)
|
||||||
|
of_dma_controller_free(pdev->dev.of_node);
|
||||||
dw_dma_off(dw);
|
dw_dma_off(dw);
|
||||||
dma_async_device_unregister(&dw->dma);
|
dma_async_device_unregister(&dw->dma);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/dw_dmac.h>
|
#include <linux/dw_dmac.h>
|
||||||
|
|
||||||
#define DW_DMA_MAX_NR_CHANNELS 8
|
#define DW_DMA_MAX_NR_CHANNELS 8
|
||||||
|
#define DW_DMA_MAX_NR_REQUESTS 16
|
||||||
|
|
||||||
/* flow controller */
|
/* flow controller */
|
||||||
enum dw_dma_fc {
|
enum dw_dma_fc {
|
||||||
|
@ -211,6 +212,8 @@ struct dw_dma_chan {
|
||||||
/* hardware configuration */
|
/* hardware configuration */
|
||||||
unsigned int block_size;
|
unsigned int block_size;
|
||||||
bool nollp;
|
bool nollp;
|
||||||
|
unsigned int request_line;
|
||||||
|
struct dw_dma_slave slave;
|
||||||
|
|
||||||
/* configuration passed via DMA_SLAVE_CONFIG */
|
/* configuration passed via DMA_SLAVE_CONFIG */
|
||||||
struct dma_slave_config dma_sconfig;
|
struct dma_slave_config dma_sconfig;
|
||||||
|
@ -239,10 +242,6 @@ struct dw_dma {
|
||||||
struct tasklet_struct tasklet;
|
struct tasklet_struct tasklet;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
/* slave information */
|
|
||||||
struct dw_dma_slave *sd;
|
|
||||||
unsigned int sd_count;
|
|
||||||
|
|
||||||
u8 all_chan_mask;
|
u8 all_chan_mask;
|
||||||
|
|
||||||
/* hardware configuration */
|
/* hardware configuration */
|
||||||
|
|
|
@ -1001,6 +1001,22 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
|
||||||
struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type);
|
struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type);
|
||||||
struct dma_chan *net_dma_find_channel(void);
|
struct dma_chan *net_dma_find_channel(void);
|
||||||
#define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
|
#define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
|
||||||
|
#define dma_request_slave_channel_compat(mask, x, y, dev, name) \
|
||||||
|
__dma_request_slave_channel_compat(&(mask), x, y, dev, name)
|
||||||
|
|
||||||
|
static inline struct dma_chan
|
||||||
|
*__dma_request_slave_channel_compat(dma_cap_mask_t *mask, dma_filter_fn fn,
|
||||||
|
void *fn_param, struct device *dev,
|
||||||
|
char *name)
|
||||||
|
{
|
||||||
|
struct dma_chan *chan;
|
||||||
|
|
||||||
|
chan = dma_request_slave_channel(dev, name);
|
||||||
|
if (chan)
|
||||||
|
return chan;
|
||||||
|
|
||||||
|
return __dma_request_channel(mask, fn, fn_param);
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Helper iov-locking functions --- */
|
/* --- Helper iov-locking functions --- */
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
*/
|
*/
|
||||||
struct dw_dma_slave {
|
struct dw_dma_slave {
|
||||||
struct device *dma_dev;
|
struct device *dma_dev;
|
||||||
const char *bus_id;
|
|
||||||
u32 cfg_hi;
|
u32 cfg_hi;
|
||||||
u32 cfg_lo;
|
u32 cfg_lo;
|
||||||
u8 src_master;
|
u8 src_master;
|
||||||
|
@ -60,9 +59,6 @@ struct dw_dma_platform_data {
|
||||||
unsigned short block_size;
|
unsigned short block_size;
|
||||||
unsigned char nr_masters;
|
unsigned char nr_masters;
|
||||||
unsigned char data_width[4];
|
unsigned char data_width[4];
|
||||||
|
|
||||||
struct dw_dma_slave *sd;
|
|
||||||
unsigned int sd_count;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* bursts size */
|
/* bursts size */
|
||||||
|
@ -114,6 +110,5 @@ void dw_dma_cyclic_stop(struct dma_chan *chan);
|
||||||
dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan);
|
dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan);
|
||||||
|
|
||||||
dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan);
|
dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan);
|
||||||
bool dw_dma_generic_filter(struct dma_chan *chan, void *param);
|
|
||||||
|
|
||||||
#endif /* DW_DMAC_H */
|
#endif /* DW_DMAC_H */
|
||||||
|
|
Loading…
Reference in New Issue