• Home
  • Raw
  • Download

Lines Matching +full:uni +full:- +full:directional

1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for the Analog Devices AXI-DMAC core
5 * Copyright 2013-2019 Analog Devices Inc.
6 * Author: Lars-Peter Clausen <lars@metafoo.de>
12 #include <linux/dma-mapping.h>
25 #include <linux/fpga/adi-axi-common.h>
27 #include <dt-bindings/dma/axi-dmac.h>
30 #include "virt-dma.h"
33 * The AXI-DMAC is a soft IP core that is used in FPGA designs. The core has
44 * runtime. By extension this means that each channel is uni-directional. It can
156 return container_of(chan->vchan.chan.device, struct axi_dmac, in chan_to_axi_dmac()
173 writel(val, axi_dmac->base + reg); in axi_dmac_write()
178 return readl(axi_dmac->base + reg); in axi_dmac_read()
183 return chan->src_type == AXI_DMAC_BUS_TYPE_AXI_MM; in axi_dmac_src_is_mem()
188 return chan->dest_type == AXI_DMAC_BUS_TYPE_AXI_MM; in axi_dmac_dest_is_mem()
195 if ((len & chan->length_align_mask) != 0) /* Not aligned */ in axi_dmac_check_len()
202 if ((addr & chan->address_align_mask) != 0) /* Not aligned */ in axi_dmac_check_addr()
220 desc = chan->next_desc; in axi_dmac_start_transfer()
223 vdesc = vchan_next_desc(&chan->vchan); in axi_dmac_start_transfer()
226 list_move_tail(&vdesc->node, &chan->active_descs); in axi_dmac_start_transfer()
229 sg = &desc->sg[desc->num_submitted]; in axi_dmac_start_transfer()
232 if (sg->id != AXI_DMAC_SG_UNUSED) { in axi_dmac_start_transfer()
233 sg->schedule_when_free = true; in axi_dmac_start_transfer()
237 desc->num_submitted++; in axi_dmac_start_transfer()
238 if (desc->num_submitted == desc->num_sgs || in axi_dmac_start_transfer()
239 desc->have_partial_xfer) { in axi_dmac_start_transfer()
240 if (desc->cyclic) in axi_dmac_start_transfer()
241 desc->num_submitted = 0; /* Start again */ in axi_dmac_start_transfer()
243 chan->next_desc = NULL; in axi_dmac_start_transfer()
246 chan->next_desc = desc; in axi_dmac_start_transfer()
249 sg->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID); in axi_dmac_start_transfer()
252 axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, sg->dest_addr); in axi_dmac_start_transfer()
253 axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, sg->dest_stride); in axi_dmac_start_transfer()
257 axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, sg->src_addr); in axi_dmac_start_transfer()
258 axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, sg->src_stride); in axi_dmac_start_transfer()
266 if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback && in axi_dmac_start_transfer()
267 desc->num_sgs == 1) in axi_dmac_start_transfer()
270 if (chan->hw_partial_xfer) in axi_dmac_start_transfer()
273 axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->x_len - 1); in axi_dmac_start_transfer()
274 axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, sg->y_len - 1); in axi_dmac_start_transfer()
281 return list_first_entry_or_null(&chan->active_descs, in axi_dmac_active_desc()
288 if (chan->hw_2d) in axi_dmac_total_sg_bytes()
289 return sg->x_len * sg->y_len; in axi_dmac_total_sg_bytes()
291 return sg->x_len; in axi_dmac_total_sg_bytes()
307 list_for_each_entry(desc, &chan->active_descs, vdesc.node) { in axi_dmac_dequeue_partial_xfers()
308 for (i = 0; i < desc->num_sgs; i++) { in axi_dmac_dequeue_partial_xfers()
309 sg = &desc->sg[i]; in axi_dmac_dequeue_partial_xfers()
310 if (sg->id == AXI_DMAC_SG_UNUSED) in axi_dmac_dequeue_partial_xfers()
312 if (sg->id == id) { in axi_dmac_dequeue_partial_xfers()
313 desc->have_partial_xfer = true; in axi_dmac_dequeue_partial_xfers()
314 sg->partial_len = len; in axi_dmac_dequeue_partial_xfers()
324 dev_dbg(dmac->dma_dev.dev, in axi_dmac_dequeue_partial_xfers()
328 dev_warn(dmac->dma_dev.dev, in axi_dmac_dequeue_partial_xfers()
343 struct dmaengine_result *rslt = &active->vdesc.tx_result; in axi_dmac_compute_residue()
344 unsigned int start = active->num_completed - 1; in axi_dmac_compute_residue()
348 rslt->result = DMA_TRANS_NOERROR; in axi_dmac_compute_residue()
349 rslt->residue = 0; in axi_dmac_compute_residue()
355 for (i = start; i < active->num_sgs; i++) { in axi_dmac_compute_residue()
356 sg = &active->sg[i]; in axi_dmac_compute_residue()
358 rslt->residue += (total - sg->partial_len); in axi_dmac_compute_residue()
373 if (chan->hw_partial_xfer && in axi_dmac_transfer_done()
378 sg = &active->sg[active->num_completed]; in axi_dmac_transfer_done()
379 if (sg->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */ in axi_dmac_transfer_done()
381 if (!(BIT(sg->id) & completed_transfers)) in axi_dmac_transfer_done()
383 active->num_completed++; in axi_dmac_transfer_done()
384 sg->id = AXI_DMAC_SG_UNUSED; in axi_dmac_transfer_done()
385 if (sg->schedule_when_free) { in axi_dmac_transfer_done()
386 sg->schedule_when_free = false; in axi_dmac_transfer_done()
390 if (sg->partial_len) in axi_dmac_transfer_done()
393 if (active->cyclic) in axi_dmac_transfer_done()
394 vchan_cyclic_callback(&active->vdesc); in axi_dmac_transfer_done()
396 if (active->num_completed == active->num_sgs || in axi_dmac_transfer_done()
397 sg->partial_len) { in axi_dmac_transfer_done()
398 if (active->cyclic) { in axi_dmac_transfer_done()
399 active->num_completed = 0; /* wrap around */ in axi_dmac_transfer_done()
401 list_del(&active->vdesc.node); in axi_dmac_transfer_done()
402 vchan_cookie_complete(&active->vdesc); in axi_dmac_transfer_done()
423 spin_lock(&dmac->chan.vchan.lock); in axi_dmac_interrupt_handler()
429 start_next = axi_dmac_transfer_done(&dmac->chan, completed); in axi_dmac_interrupt_handler()
433 axi_dmac_start_transfer(&dmac->chan); in axi_dmac_interrupt_handler()
434 spin_unlock(&dmac->chan.vchan.lock); in axi_dmac_interrupt_handler()
446 spin_lock_irqsave(&chan->vchan.lock, flags); in axi_dmac_terminate_all()
448 chan->next_desc = NULL; in axi_dmac_terminate_all()
449 vchan_get_all_descriptors(&chan->vchan, &head); in axi_dmac_terminate_all()
450 list_splice_tail_init(&chan->active_descs, &head); in axi_dmac_terminate_all()
451 spin_unlock_irqrestore(&chan->vchan.lock, flags); in axi_dmac_terminate_all()
453 vchan_dma_desc_free_list(&chan->vchan, &head); in axi_dmac_terminate_all()
462 vchan_synchronize(&chan->vchan); in axi_dmac_synchronize()
473 spin_lock_irqsave(&chan->vchan.lock, flags); in axi_dmac_issue_pending()
474 if (vchan_issue_pending(&chan->vchan)) in axi_dmac_issue_pending()
476 spin_unlock_irqrestore(&chan->vchan.lock, flags); in axi_dmac_issue_pending()
489 desc->sg[i].id = AXI_DMAC_SG_UNUSED; in axi_dmac_alloc_desc()
491 desc->num_sgs = num_sgs; in axi_dmac_alloc_desc()
506 num_segments = DIV_ROUND_UP(period_len, chan->max_length); in axi_dmac_fill_linear_sg()
509 segment_size = ((segment_size - 1) | chan->length_align_mask) + 1; in axi_dmac_fill_linear_sg()
516 sg->dest_addr = addr; in axi_dmac_fill_linear_sg()
518 sg->src_addr = addr; in axi_dmac_fill_linear_sg()
519 sg->x_len = segment_size; in axi_dmac_fill_linear_sg()
520 sg->y_len = 1; in axi_dmac_fill_linear_sg()
523 len -= segment_size; in axi_dmac_fill_linear_sg()
527 sg->dest_addr = addr; in axi_dmac_fill_linear_sg()
529 sg->src_addr = addr; in axi_dmac_fill_linear_sg()
530 sg->x_len = len; in axi_dmac_fill_linear_sg()
531 sg->y_len = 1; in axi_dmac_fill_linear_sg()
551 if (direction != chan->direction) in axi_dmac_prep_slave_sg()
556 num_sgs += DIV_ROUND_UP(sg_dma_len(sg), chan->max_length); in axi_dmac_prep_slave_sg()
562 dsg = desc->sg; in axi_dmac_prep_slave_sg()
575 desc->cyclic = false; in axi_dmac_prep_slave_sg()
577 return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); in axi_dmac_prep_slave_sg()
589 if (direction != chan->direction) in axi_dmac_prep_dma_cyclic()
600 num_segments = DIV_ROUND_UP(period_len, chan->max_length); in axi_dmac_prep_dma_cyclic()
607 period_len, desc->sg); in axi_dmac_prep_dma_cyclic()
609 desc->cyclic = true; in axi_dmac_prep_dma_cyclic()
611 return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); in axi_dmac_prep_dma_cyclic()
622 if (xt->frame_size != 1) in axi_dmac_prep_interleaved()
625 if (xt->dir != chan->direction) in axi_dmac_prep_interleaved()
629 if (!xt->src_inc || !axi_dmac_check_addr(chan, xt->src_start)) in axi_dmac_prep_interleaved()
634 if (!xt->dst_inc || !axi_dmac_check_addr(chan, xt->dst_start)) in axi_dmac_prep_interleaved()
638 dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); in axi_dmac_prep_interleaved()
639 src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); in axi_dmac_prep_interleaved()
641 if (chan->hw_2d) { in axi_dmac_prep_interleaved()
642 if (!axi_dmac_check_len(chan, xt->sgl[0].size) || in axi_dmac_prep_interleaved()
643 xt->numf == 0) in axi_dmac_prep_interleaved()
645 if (xt->sgl[0].size + dst_icg > chan->max_length || in axi_dmac_prep_interleaved()
646 xt->sgl[0].size + src_icg > chan->max_length) in axi_dmac_prep_interleaved()
651 if (chan->max_length / xt->sgl[0].size < xt->numf) in axi_dmac_prep_interleaved()
653 if (!axi_dmac_check_len(chan, xt->sgl[0].size * xt->numf)) in axi_dmac_prep_interleaved()
662 desc->sg[0].src_addr = xt->src_start; in axi_dmac_prep_interleaved()
663 desc->sg[0].src_stride = xt->sgl[0].size + src_icg; in axi_dmac_prep_interleaved()
667 desc->sg[0].dest_addr = xt->dst_start; in axi_dmac_prep_interleaved()
668 desc->sg[0].dest_stride = xt->sgl[0].size + dst_icg; in axi_dmac_prep_interleaved()
671 if (chan->hw_2d) { in axi_dmac_prep_interleaved()
672 desc->sg[0].x_len = xt->sgl[0].size; in axi_dmac_prep_interleaved()
673 desc->sg[0].y_len = xt->numf; in axi_dmac_prep_interleaved()
675 desc->sg[0].x_len = xt->sgl[0].size * xt->numf; in axi_dmac_prep_interleaved()
676 desc->sg[0].y_len = 1; in axi_dmac_prep_interleaved()
680 desc->cyclic = true; in axi_dmac_prep_interleaved()
682 return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); in axi_dmac_prep_interleaved()
735 chan->address_align_mask = max(chan->dest_width, chan->src_width) - 1; in axi_dmac_adjust_chan_params()
738 chan->direction = DMA_MEM_TO_MEM; in axi_dmac_adjust_chan_params()
740 chan->direction = DMA_MEM_TO_DEV; in axi_dmac_adjust_chan_params()
742 chan->direction = DMA_DEV_TO_MEM; in axi_dmac_adjust_chan_params()
744 chan->direction = DMA_DEV_TO_DEV; in axi_dmac_adjust_chan_params()
764 return -EINVAL; in axi_dmac_parse_chan_dt()
766 ret = of_property_read_u32(of_chan, "adi,source-bus-type", &val); in axi_dmac_parse_chan_dt()
770 return -EINVAL; in axi_dmac_parse_chan_dt()
771 chan->src_type = val; in axi_dmac_parse_chan_dt()
773 ret = of_property_read_u32(of_chan, "adi,destination-bus-type", &val); in axi_dmac_parse_chan_dt()
777 return -EINVAL; in axi_dmac_parse_chan_dt()
778 chan->dest_type = val; in axi_dmac_parse_chan_dt()
780 ret = of_property_read_u32(of_chan, "adi,source-bus-width", &val); in axi_dmac_parse_chan_dt()
783 chan->src_width = val / 8; in axi_dmac_parse_chan_dt()
785 ret = of_property_read_u32(of_chan, "adi,destination-bus-width", &val); in axi_dmac_parse_chan_dt()
788 chan->dest_width = val / 8; in axi_dmac_parse_chan_dt()
800 of_channels = of_get_child_by_name(dev->of_node, "adi,channels"); in axi_dmac_parse_dt()
802 return -ENODEV; in axi_dmac_parse_dt()
805 ret = axi_dmac_parse_chan_dt(of_chan, &dmac->chan); in axi_dmac_parse_dt()
809 return -EINVAL; in axi_dmac_parse_dt()
819 struct axi_dmac_chan *chan = &dmac->chan; in axi_dmac_read_chan_config()
825 return -EFAULT; in axi_dmac_read_chan_config()
831 return -EINVAL; in axi_dmac_read_chan_config()
833 chan->src_type = val; in axi_dmac_read_chan_config()
838 return -EINVAL; in axi_dmac_read_chan_config()
840 chan->dest_type = val; in axi_dmac_read_chan_config()
845 return -EINVAL; in axi_dmac_read_chan_config()
848 chan->src_width = 1 << val; in axi_dmac_read_chan_config()
853 return -EINVAL; in axi_dmac_read_chan_config()
855 chan->dest_width = 1 << val; in axi_dmac_read_chan_config()
864 struct axi_dmac_chan *chan = &dmac->chan; in axi_dmac_detect_caps()
868 chan->hw_cyclic = true; in axi_dmac_detect_caps()
872 chan->hw_2d = true; in axi_dmac_detect_caps()
875 chan->max_length = axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH); in axi_dmac_detect_caps()
876 if (chan->max_length != UINT_MAX) in axi_dmac_detect_caps()
877 chan->max_length++; in axi_dmac_detect_caps()
881 chan->dest_type == AXI_DMAC_BUS_TYPE_AXI_MM) { in axi_dmac_detect_caps()
882 dev_err(dmac->dma_dev.dev, in axi_dmac_detect_caps()
883 "Destination memory-mapped interface not supported."); in axi_dmac_detect_caps()
884 return -ENODEV; in axi_dmac_detect_caps()
889 chan->src_type == AXI_DMAC_BUS_TYPE_AXI_MM) { in axi_dmac_detect_caps()
890 dev_err(dmac->dma_dev.dev, in axi_dmac_detect_caps()
891 "Source memory-mapped interface not supported."); in axi_dmac_detect_caps()
892 return -ENODEV; in axi_dmac_detect_caps()
896 chan->hw_partial_xfer = true; in axi_dmac_detect_caps()
900 chan->length_align_mask = in axi_dmac_detect_caps()
903 chan->length_align_mask = chan->address_align_mask; in axi_dmac_detect_caps()
917 dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); in axi_dmac_probe()
919 return -ENOMEM; in axi_dmac_probe()
921 dmac->irq = platform_get_irq(pdev, 0); in axi_dmac_probe()
922 if (dmac->irq < 0) in axi_dmac_probe()
923 return dmac->irq; in axi_dmac_probe()
924 if (dmac->irq == 0) in axi_dmac_probe()
925 return -EINVAL; in axi_dmac_probe()
927 dmac->base = devm_platform_ioremap_resource(pdev, 0); in axi_dmac_probe()
928 if (IS_ERR(dmac->base)) in axi_dmac_probe()
929 return PTR_ERR(dmac->base); in axi_dmac_probe()
931 dmac->clk = devm_clk_get(&pdev->dev, NULL); in axi_dmac_probe()
932 if (IS_ERR(dmac->clk)) in axi_dmac_probe()
933 return PTR_ERR(dmac->clk); in axi_dmac_probe()
935 ret = clk_prepare_enable(dmac->clk); in axi_dmac_probe()
942 ret = axi_dmac_read_chan_config(&pdev->dev, dmac); in axi_dmac_probe()
944 ret = axi_dmac_parse_dt(&pdev->dev, dmac); in axi_dmac_probe()
949 INIT_LIST_HEAD(&dmac->chan.active_descs); in axi_dmac_probe()
951 dma_set_max_seg_size(&pdev->dev, UINT_MAX); in axi_dmac_probe()
953 dma_dev = &dmac->dma_dev; in axi_dmac_probe()
954 dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); in axi_dmac_probe()
955 dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask); in axi_dmac_probe()
956 dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask); in axi_dmac_probe()
957 dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources; in axi_dmac_probe()
958 dma_dev->device_tx_status = dma_cookie_status; in axi_dmac_probe()
959 dma_dev->device_issue_pending = axi_dmac_issue_pending; in axi_dmac_probe()
960 dma_dev->device_prep_slave_sg = axi_dmac_prep_slave_sg; in axi_dmac_probe()
961 dma_dev->device_prep_dma_cyclic = axi_dmac_prep_dma_cyclic; in axi_dmac_probe()
962 dma_dev->device_prep_interleaved_dma = axi_dmac_prep_interleaved; in axi_dmac_probe()
963 dma_dev->device_terminate_all = axi_dmac_terminate_all; in axi_dmac_probe()
964 dma_dev->device_synchronize = axi_dmac_synchronize; in axi_dmac_probe()
965 dma_dev->dev = &pdev->dev; in axi_dmac_probe()
966 dma_dev->src_addr_widths = BIT(dmac->chan.src_width); in axi_dmac_probe()
967 dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width); in axi_dmac_probe()
968 dma_dev->directions = BIT(dmac->chan.direction); in axi_dmac_probe()
969 dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; in axi_dmac_probe()
970 INIT_LIST_HEAD(&dma_dev->channels); in axi_dmac_probe()
972 dmac->chan.vchan.desc_free = axi_dmac_desc_free; in axi_dmac_probe()
973 vchan_init(&dmac->chan.vchan, dma_dev); in axi_dmac_probe()
979 dma_dev->copy_align = (dmac->chan.address_align_mask + 1); in axi_dmac_probe()
983 if (of_dma_is_coherent(pdev->dev.of_node)) { in axi_dmac_probe()
988 dev_err(dmac->dma_dev.dev, in axi_dmac_probe()
990 ret = -EINVAL; in axi_dmac_probe()
999 ret = of_dma_controller_register(pdev->dev.of_node, in axi_dmac_probe()
1004 ret = request_irq(dmac->irq, axi_dmac_interrupt_handler, IRQF_SHARED, in axi_dmac_probe()
1005 dev_name(&pdev->dev), dmac); in axi_dmac_probe()
1011 regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base, in axi_dmac_probe()
1021 free_irq(dmac->irq, dmac); in axi_dmac_probe()
1023 of_dma_controller_free(pdev->dev.of_node); in axi_dmac_probe()
1025 dma_async_device_unregister(&dmac->dma_dev); in axi_dmac_probe()
1027 clk_disable_unprepare(dmac->clk); in axi_dmac_probe()
1036 free_irq(dmac->irq, dmac); in axi_dmac_remove()
1037 of_dma_controller_free(pdev->dev.of_node); in axi_dmac_remove()
1038 tasklet_kill(&dmac->chan.vchan.task); in axi_dmac_remove()
1039 dma_async_device_unregister(&dmac->dma_dev); in axi_dmac_remove()
1040 clk_disable_unprepare(dmac->clk); in axi_dmac_remove()
1046 { .compatible = "adi,axi-dmac-1.00.a" },
1053 .name = "dma-axi-dmac",
1061 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
1062 MODULE_DESCRIPTION("DMA controller driver for the AXI-DMAC controller");