diff options
Diffstat (limited to 'drivers/dma/at_hdmac.c')
-rw-r--r-- | drivers/dma/at_hdmac.c | 214 |
1 files changed, 140 insertions, 74 deletions
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index fcfa0a8b5c59..7aa58d204892 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -23,8 +23,11 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "at_hdmac_regs.h" +#include "dmaengine.h" /* * Glossary @@ -190,27 +193,6 @@ static void atc_desc_chain(struct at_desc **first, struct at_desc **prev, } /** - * atc_assign_cookie - compute and assign new cookie - * @atchan: channel we work on - * @desc: descriptor to assign cookie for - * - * Called with atchan->lock held and bh disabled - */ -static dma_cookie_t -atc_assign_cookie(struct at_dma_chan *atchan, struct at_desc *desc) -{ - dma_cookie_t cookie = atchan->chan_common.cookie; - - if (++cookie < 0) - cookie = 1; - - atchan->chan_common.cookie = cookie; - desc->txd.cookie = cookie; - - return cookie; -} - -/** * atc_dostart - starts the DMA engine for real * @atchan: the channel we want to start * @first: first descriptor in the list we want to begin with @@ -267,7 +249,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) dev_vdbg(chan2dev(&atchan->chan_common), "descriptor %u complete\n", txd->cookie); - atchan->completed_cookie = txd->cookie; + dma_cookie_complete(txd); /* move children to free_list */ list_splice_init(&desc->tx_list, &atchan->free_list); @@ -545,7 +527,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) unsigned long flags; spin_lock_irqsave(&atchan->lock, flags); - cookie = atc_assign_cookie(atchan, desc); + cookie = dma_cookie_assign(tx); if (list_empty(&atchan->active_list)) { dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n", @@ -657,14 +639,16 @@ err_desc_get: * @sg_len: number of entries in @scatterlist * @direction: DMA direction * @flags: tx descriptor status flags + * @context: transaction context (ignored) */ static struct dma_async_tx_descriptor * atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, - unsigned long flags) + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; + struct dma_slave_config *sconfig = &atchan->dma_sconfig; struct at_desc *first = NULL; struct at_desc *prev = NULL; u32 ctrla; @@ -678,7 +662,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dev_vdbg(chan2dev(chan), "prep_slave_sg (%d): %s f0x%lx\n", sg_len, - direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", + direction == DMA_MEM_TO_DEV ? "TO DEVICE" : "FROM DEVICE", flags); if (unlikely(!atslave || !sg_len)) { @@ -686,19 +670,18 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } - reg_width = atslave->reg_width; - ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; ctrlb = ATC_IEN; switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: + reg_width = convert_buswidth(sconfig->dst_addr_width); ctrla |= ATC_DST_WIDTH(reg_width); ctrlb |= ATC_DST_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_INCR | ATC_FC_MEM2PER | ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF); - reg = atslave->tx_reg; + reg = sconfig->dst_addr; for_each_sg(sgl, sg, sg_len, i) { struct at_desc *desc; u32 len; @@ -725,14 +708,15 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, total_len += len; } break; - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: + reg_width = convert_buswidth(sconfig->src_addr_width); ctrla |= ATC_SRC_WIDTH(reg_width); ctrlb |= ATC_DST_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_FIXED | ATC_FC_PER2MEM | ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF); - reg = atslave->rx_reg; + reg = sconfig->src_addr; for_each_sg(sgl, sg, sg_len, i) { struct at_desc *desc; u32 len; @@ -787,7 +771,7 @@ err_desc_get: */ static int atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { if (period_len > (ATC_BTSIZE_MAX << reg_width)) goto err_out; @@ -795,7 +779,7 @@ atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, goto err_out; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto err_out; - if (unlikely(!(direction & (DMA_TO_DEVICE | DMA_FROM_DEVICE)))) + if (unlikely(!(direction & (DMA_DEV_TO_MEM | DMA_MEM_TO_DEV)))) goto err_out; return 0; @@ -808,12 +792,15 @@ err_out: * atc_dma_cyclic_fill_desc - Fill one period decriptor */ static int -atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, +atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, unsigned int period_index, dma_addr_t buf_addr, - size_t period_len, enum dma_data_direction direction) + unsigned int reg_width, size_t period_len, + enum dma_transfer_direction direction) { - u32 ctrla; - unsigned int reg_width = atslave->reg_width; + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma_slave *atslave = chan->private; + struct dma_slave_config *sconfig = &atchan->dma_sconfig; + u32 ctrla; /* prepare common CRTLA value */ ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla @@ -822,9 +809,9 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, | period_len >> reg_width; switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: desc->lli.saddr = buf_addr + (period_len * period_index); - desc->lli.daddr = atslave->tx_reg; + desc->lli.daddr = sconfig->dst_addr; desc->lli.ctrla = ctrla; desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_INCR @@ -833,8 +820,8 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, | ATC_DIF(AT_DMA_PER_IF); break; - case DMA_FROM_DEVICE: - desc->lli.saddr = atslave->rx_reg; + case DMA_DEV_TO_MEM: + desc->lli.saddr = sconfig->src_addr; desc->lli.daddr = buf_addr + (period_len * period_index); desc->lli.ctrla = ctrla; desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR @@ -858,21 +845,25 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, * @buf_len: total number of bytes for the entire buffer * @period_len: number of bytes for each period * @direction: transfer direction, to or from device + * @context: transfer context (ignored) */ static struct dma_async_tx_descriptor * atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; + struct dma_slave_config *sconfig = &atchan->dma_sconfig; struct at_desc *first = NULL; struct at_desc *prev = NULL; unsigned long was_cyclic; + unsigned int reg_width; unsigned int periods = buf_len / period_len; unsigned int i; dev_vdbg(chan2dev(chan), "prep_dma_cyclic: %s buf@0x%08x - %d (%d/%d)\n", - direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", + direction == DMA_MEM_TO_DEV ? "TO DEVICE" : "FROM DEVICE", buf_addr, periods, buf_len, period_len); @@ -887,8 +878,13 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, return NULL; } + if (sconfig->direction == DMA_MEM_TO_DEV) + reg_width = convert_buswidth(sconfig->dst_addr_width); + else + reg_width = convert_buswidth(sconfig->src_addr_width); + /* Check for too big/unaligned periods and unaligned DMA buffer */ - if (atc_dma_cyclic_check_values(atslave->reg_width, buf_addr, + if (atc_dma_cyclic_check_values(reg_width, buf_addr, period_len, direction)) goto err_out; @@ -900,8 +896,8 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, if (!desc) goto err_desc_get; - if (atc_dma_cyclic_fill_desc(atslave, desc, i, buf_addr, - period_len, direction)) + if (atc_dma_cyclic_fill_desc(chan, desc, i, buf_addr, + reg_width, period_len, direction)) goto err_desc_get; atc_desc_chain(&first, &prev, desc); @@ -924,6 +920,23 @@ err_out: return NULL; } +static int set_runtime_config(struct dma_chan *chan, + struct dma_slave_config *sconfig) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + /* Check if it is chan is configured for slave transfers */ + if (!chan->private) + return -EINVAL; + + memcpy(&atchan->dma_sconfig, sconfig, sizeof(*sconfig)); + + convert_burst(&atchan->dma_sconfig.src_maxburst); + convert_burst(&atchan->dma_sconfig.dst_maxburst); + + return 0; +} + static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) @@ -984,6 +997,8 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, clear_bit(ATC_IS_CYCLIC, &atchan->status); spin_unlock_irqrestore(&atchan->lock, flags); + } else if (cmd == DMA_SLAVE_CONFIG) { + return set_runtime_config(chan, (struct dma_slave_config *)arg); } else { return -ENXIO; } @@ -1014,26 +1029,20 @@ atc_tx_status(struct dma_chan *chan, spin_lock_irqsave(&atchan->lock, flags); - last_complete = atchan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret != DMA_SUCCESS) { atc_cleanup_descriptors(atchan); - last_complete = atchan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); } + last_complete = chan->completed_cookie; + last_used = chan->cookie; + spin_unlock_irqrestore(&atchan->lock, flags); if (ret != DMA_SUCCESS) - dma_set_tx_state(txstate, last_complete, last_used, - atc_first_active(atchan)->len); - else - dma_set_tx_state(txstate, last_complete, last_used, 0); + dma_set_residue(txstate, atc_first_active(atchan)->len); if (atc_chan_is_paused(atchan)) ret = DMA_PAUSED; @@ -1127,7 +1136,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; list_splice(&tmp_list, &atchan->free_list); - atchan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); spin_unlock_irqrestore(&atchan->lock, flags); /* channel parameters */ @@ -1175,6 +1184,56 @@ static void atc_free_chan_resources(struct dma_chan *chan) /*-- Module Management -----------------------------------------------*/ +/* cap_mask is a multi-u32 bitfield, fill it with proper C code. */ +static struct at_dma_platform_data at91sam9rl_config = { + .nr_channels = 2, +}; +static struct at_dma_platform_data at91sam9g45_config = { + .nr_channels = 8, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id atmel_dma_dt_ids[] = { + { + .compatible = "atmel,at91sam9rl-dma", + .data = &at91sam9rl_config, + }, { + .compatible = "atmel,at91sam9g45-dma", + .data = &at91sam9g45_config, + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, atmel_dma_dt_ids); +#endif + +static const struct platform_device_id atdma_devtypes[] = { + { + .name = "at91sam9rl_dma", + .driver_data = (unsigned long) &at91sam9rl_config, + }, { + .name = "at91sam9g45_dma", + .driver_data = (unsigned long) &at91sam9g45_config, + }, { + /* sentinel */ + } +}; + +static inline struct at_dma_platform_data * __init at_dma_get_driver_data( + struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(atmel_dma_dt_ids, pdev->dev.of_node); + if (match == NULL) + return NULL; + return match->data; + } + return (struct at_dma_platform_data *) + platform_get_device_id(pdev)->driver_data; +} + /** * at_dma_off - disable DMA controller * @atdma: the Atmel HDAMC device @@ -1193,18 +1252,23 @@ static void at_dma_off(struct at_dma *atdma) static int __init at_dma_probe(struct platform_device *pdev) { - struct at_dma_platform_data *pdata; struct resource *io; struct at_dma *atdma; size_t size; int irq; int err; int i; + struct at_dma_platform_data *plat_dat; - /* get DMA Controller parameters from platform */ - pdata = pdev->dev.platform_data; - if (!pdata || pdata->nr_channels > AT_DMA_MAX_NR_CHANNELS) - return -EINVAL; + /* setup platform data for each SoC */ + dma_cap_set(DMA_MEMCPY, at91sam9rl_config.cap_mask); + dma_cap_set(DMA_MEMCPY, at91sam9g45_config.cap_mask); + dma_cap_set(DMA_SLAVE, at91sam9g45_config.cap_mask); + + /* get DMA parameters from controller type */ + plat_dat = at_dma_get_driver_data(pdev); + if (!plat_dat) + return -ENODEV; io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) @@ -1215,14 +1279,14 @@ static int __init at_dma_probe(struct platform_device *pdev) return irq; size = sizeof(struct at_dma); - size += pdata->nr_channels * sizeof(struct at_dma_chan); + size += plat_dat->nr_channels * sizeof(struct at_dma_chan); atdma = kzalloc(size, GFP_KERNEL); if (!atdma) return -ENOMEM; - /* discover transaction capabilites from the platform data */ - atdma->dma_common.cap_mask = pdata->cap_mask; - atdma->all_chan_mask = (1 << pdata->nr_channels) - 1; + /* discover transaction capabilities */ + atdma->dma_common.cap_mask = plat_dat->cap_mask; + atdma->all_chan_mask = (1 << plat_dat->nr_channels) - 1; size = resource_size(io); if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { @@ -1268,11 +1332,11 @@ static int __init at_dma_probe(struct platform_device *pdev) /* initialize channels related values */ INIT_LIST_HEAD(&atdma->dma_common.channels); - for (i = 0; i < pdata->nr_channels; i++) { + for (i = 0; i < plat_dat->nr_channels; i++) { struct at_dma_chan *atchan = &atdma->chan[i]; atchan->chan_common.device = &atdma->dma_common; - atchan->chan_common.cookie = atchan->completed_cookie = 1; + dma_cookie_init(&atchan->chan_common); list_add_tail(&atchan->chan_common.device_node, &atdma->dma_common.channels); @@ -1286,7 +1350,7 @@ static int __init at_dma_probe(struct platform_device *pdev) tasklet_init(&atchan->tasklet, atc_tasklet, (unsigned long)atchan); - atc_enable_irq(atchan); + atc_enable_chan_irq(atdma, i); } /* set base routines */ @@ -1313,7 +1377,7 @@ static int __init at_dma_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n", dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "", dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "", - pdata->nr_channels); + plat_dat->nr_channels); dma_async_device_register(&atdma->dma_common); @@ -1353,7 +1417,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) struct at_dma_chan *atchan = to_at_dma_chan(chan); /* Disable interrupts */ - atc_disable_irq(atchan); + atc_disable_chan_irq(atdma, chan->chan_id); tasklet_disable(&atchan->tasklet); tasklet_kill(&atchan->tasklet); @@ -1495,9 +1559,11 @@ static const struct dev_pm_ops at_dma_dev_pm_ops = { static struct platform_driver at_dma_driver = { .remove = __exit_p(at_dma_remove), .shutdown = at_dma_shutdown, + .id_table = atdma_devtypes, .driver = { .name = "at_hdmac", .pm = &at_dma_dev_pm_ops, + .of_match_table = of_match_ptr(atmel_dma_dt_ids), }, }; |