diff options
| author | Alexander Popov <a13xp0p0v88@gmail.com> | 2014-05-15 10:15:32 -0400 |
|---|---|---|
| committer | Vinod Koul <vinod.koul@intel.com> | 2014-05-22 01:07:01 -0400 |
| commit | 63da8e0d4f274fdf73b9924e8fd8f64a3d11d24a (patch) | |
| tree | 29080d480bb3ae16f12570c26f696553dec3d56d | |
| parent | ba730340f96c01160b5f26f81e8fb38f8cb1821c (diff) | |
dmaengine: mpc512x: add support for peripheral transfers
Introduce support for slave s/g transfer preparation and the associated
device control callback in the MPC512x DMA controller driver, which adds
support for data transfers between memory and peripheral I/O to the
previously supported mem-to-mem transfers.
Signed-off-by: Alexander Popov <a13xp0p0v88@gmail.com>
[fixed subsytem name]
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
| -rw-r--r-- | drivers/dma/mpc512x_dma.c | 244 |
1 files changed, 239 insertions, 5 deletions
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 96104f4eebe8..2ad43738ac8b 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008. | 2 | * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008. |
| 3 | * Copyright (C) Semihalf 2009 | 3 | * Copyright (C) Semihalf 2009 |
| 4 | * Copyright (C) Ilya Yanok, Emcraft Systems 2010 | 4 | * Copyright (C) Ilya Yanok, Emcraft Systems 2010 |
| 5 | * Copyright (C) Alexander Popov, Promcontroller 2014 | ||
| 5 | * | 6 | * |
| 6 | * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description | 7 | * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description |
| 7 | * (defines, structures and comments) was taken from MPC5121 DMA driver | 8 | * (defines, structures and comments) was taken from MPC5121 DMA driver |
| @@ -29,8 +30,18 @@ | |||
| 29 | */ | 30 | */ |
| 30 | 31 | ||
| 31 | /* | 32 | /* |
| 32 | * This is initial version of MPC5121 DMA driver. Only memory to memory | 33 | * MPC512x and MPC8308 DMA driver. It supports |
| 33 | * transfers are supported (tested using dmatest module). | 34 | * memory to memory data transfers (tested using dmatest module) and |
| 35 | * data transfers between memory and peripheral I/O memory | ||
| 36 | * by means of slave scatter/gather with these limitations: | ||
| 37 | * - chunked transfers (described by s/g lists with more than one item) | ||
| 38 | * are refused as long as proper support for scatter/gather is missing; | ||
| 39 | * - transfers on MPC8308 always start from software as this SoC appears | ||
| 40 | * not to have external request lines for peripheral flow control; | ||
| 41 | * - only peripheral devices with 4-byte FIFO access register are supported; | ||
| 42 | * - minimal memory <-> I/O memory transfer chunk is 4 bytes and consequently | ||
| 43 | * source and destination addresses must be 4-byte aligned | ||
| 44 | * and transfer size must be aligned on (4 * maxburst) boundary; | ||
| 34 | */ | 45 | */ |
| 35 | 46 | ||
| 36 | #include <linux/module.h> | 47 | #include <linux/module.h> |
| @@ -189,6 +200,7 @@ struct mpc_dma_desc { | |||
| 189 | dma_addr_t tcd_paddr; | 200 | dma_addr_t tcd_paddr; |
| 190 | int error; | 201 | int error; |
| 191 | struct list_head node; | 202 | struct list_head node; |
| 203 | int will_access_peripheral; | ||
| 192 | }; | 204 | }; |
| 193 | 205 | ||
| 194 | struct mpc_dma_chan { | 206 | struct mpc_dma_chan { |
| @@ -201,6 +213,12 @@ struct mpc_dma_chan { | |||
| 201 | struct mpc_dma_tcd *tcd; | 213 | struct mpc_dma_tcd *tcd; |
| 202 | dma_addr_t tcd_paddr; | 214 | dma_addr_t tcd_paddr; |
| 203 | 215 | ||
| 216 | /* Settings for access to peripheral FIFO */ | ||
| 217 | dma_addr_t src_per_paddr; | ||
| 218 | u32 src_tcd_nunits; | ||
| 219 | dma_addr_t dst_per_paddr; | ||
| 220 | u32 dst_tcd_nunits; | ||
| 221 | |||
| 204 | /* Lock for this structure */ | 222 | /* Lock for this structure */ |
| 205 | spinlock_t lock; | 223 | spinlock_t lock; |
| 206 | }; | 224 | }; |
| @@ -251,8 +269,23 @@ static void mpc_dma_execute(struct mpc_dma_chan *mchan) | |||
| 251 | struct mpc_dma_desc *mdesc; | 269 | struct mpc_dma_desc *mdesc; |
| 252 | int cid = mchan->chan.chan_id; | 270 | int cid = mchan->chan.chan_id; |
| 253 | 271 | ||
| 254 | /* Move all queued descriptors to active list */ | 272 | while (!list_empty(&mchan->queued)) { |
| 255 | list_splice_tail_init(&mchan->queued, &mchan->active); | 273 | mdesc = list_first_entry(&mchan->queued, |
| 274 | struct mpc_dma_desc, node); | ||
| 275 | /* | ||
| 276 | * Grab either several mem-to-mem transfer descriptors | ||
| 277 | * or one peripheral transfer descriptor, | ||
| 278 | * don't mix mem-to-mem and peripheral transfer descriptors | ||
| 279 | * within the same 'active' list. | ||
| 280 | */ | ||
| 281 | if (mdesc->will_access_peripheral) { | ||
| 282 | if (list_empty(&mchan->active)) | ||
| 283 | list_move_tail(&mdesc->node, &mchan->active); | ||
| 284 | break; | ||
| 285 | } else { | ||
| 286 | list_move_tail(&mdesc->node, &mchan->active); | ||
| 287 | } | ||
| 288 | } | ||
| 256 | 289 | ||
| 257 | /* Chain descriptors into one transaction */ | 290 | /* Chain descriptors into one transaction */ |
| 258 | list_for_each_entry(mdesc, &mchan->active, node) { | 291 | list_for_each_entry(mdesc, &mchan->active, node) { |
| @@ -278,7 +311,17 @@ static void mpc_dma_execute(struct mpc_dma_chan *mchan) | |||
| 278 | 311 | ||
| 279 | if (first != prev) | 312 | if (first != prev) |
| 280 | mdma->tcd[cid].e_sg = 1; | 313 | mdma->tcd[cid].e_sg = 1; |
| 281 | out_8(&mdma->regs->dmassrt, cid); | 314 | |
| 315 | if (mdma->is_mpc8308) { | ||
| 316 | /* MPC8308, no request lines, software initiated start */ | ||
| 317 | out_8(&mdma->regs->dmassrt, cid); | ||
| 318 | } else if (first->will_access_peripheral) { | ||
| 319 | /* Peripherals involved, start by external request signal */ | ||
| 320 | out_8(&mdma->regs->dmaserq, cid); | ||
| 321 | } else { | ||
| 322 | /* Memory to memory transfer, software initiated start */ | ||
| 323 | out_8(&mdma->regs->dmassrt, cid); | ||
| 324 | } | ||
| 282 | } | 325 | } |
| 283 | 326 | ||
| 284 | /* Handle interrupt on one half of DMA controller (32 channels) */ | 327 | /* Handle interrupt on one half of DMA controller (32 channels) */ |
| @@ -596,6 +639,7 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, | |||
| 596 | } | 639 | } |
| 597 | 640 | ||
| 598 | mdesc->error = 0; | 641 | mdesc->error = 0; |
| 642 | mdesc->will_access_peripheral = 0; | ||
| 599 | tcd = mdesc->tcd; | 643 | tcd = mdesc->tcd; |
| 600 | 644 | ||
| 601 | /* Prepare Transfer Control Descriptor for this transaction */ | 645 | /* Prepare Transfer Control Descriptor for this transaction */ |
| @@ -643,6 +687,193 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, | |||
| 643 | return &mdesc->desc; | 687 | return &mdesc->desc; |
| 644 | } | 688 | } |
| 645 | 689 | ||
| 690 | static struct dma_async_tx_descriptor * | ||
| 691 | mpc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | ||
| 692 | unsigned int sg_len, enum dma_transfer_direction direction, | ||
| 693 | unsigned long flags, void *context) | ||
| 694 | { | ||
| 695 | struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan); | ||
| 696 | struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan); | ||
| 697 | struct mpc_dma_desc *mdesc = NULL; | ||
| 698 | dma_addr_t per_paddr; | ||
| 699 | u32 tcd_nunits; | ||
| 700 | struct mpc_dma_tcd *tcd; | ||
| 701 | unsigned long iflags; | ||
| 702 | struct scatterlist *sg; | ||
| 703 | size_t len; | ||
| 704 | int iter, i; | ||
| 705 | |||
| 706 | /* Currently there is no proper support for scatter/gather */ | ||
| 707 | if (sg_len != 1) | ||
| 708 | return NULL; | ||
| 709 | |||
| 710 | if (!is_slave_direction(direction)) | ||
| 711 | return NULL; | ||
| 712 | |||
| 713 | for_each_sg(sgl, sg, sg_len, i) { | ||
| 714 | spin_lock_irqsave(&mchan->lock, iflags); | ||
| 715 | |||
| 716 | mdesc = list_first_entry(&mchan->free, | ||
| 717 | struct mpc_dma_desc, node); | ||
| 718 | if (!mdesc) { | ||
| 719 | spin_unlock_irqrestore(&mchan->lock, iflags); | ||
| 720 | /* Try to free completed descriptors */ | ||
| 721 | mpc_dma_process_completed(mdma); | ||
| 722 | return NULL; | ||
| 723 | } | ||
| 724 | |||
| 725 | list_del(&mdesc->node); | ||
| 726 | |||
| 727 | if (direction == DMA_DEV_TO_MEM) { | ||
| 728 | per_paddr = mchan->src_per_paddr; | ||
| 729 | tcd_nunits = mchan->src_tcd_nunits; | ||
| 730 | } else { | ||
| 731 | per_paddr = mchan->dst_per_paddr; | ||
| 732 | tcd_nunits = mchan->dst_tcd_nunits; | ||
| 733 | } | ||
| 734 | |||
| 735 | spin_unlock_irqrestore(&mchan->lock, iflags); | ||
| 736 | |||
| 737 | if (per_paddr == 0 || tcd_nunits == 0) | ||
| 738 | goto err_prep; | ||
| 739 | |||
| 740 | mdesc->error = 0; | ||
| 741 | mdesc->will_access_peripheral = 1; | ||
| 742 | |||
| 743 | /* Prepare Transfer Control Descriptor for this transaction */ | ||
| 744 | tcd = mdesc->tcd; | ||
| 745 | |||
| 746 | memset(tcd, 0, sizeof(struct mpc_dma_tcd)); | ||
| 747 | |||
| 748 | if (!IS_ALIGNED(sg_dma_address(sg), 4)) | ||
| 749 | goto err_prep; | ||
| 750 | |||
| 751 | if (direction == DMA_DEV_TO_MEM) { | ||
| 752 | tcd->saddr = per_paddr; | ||
| 753 | tcd->daddr = sg_dma_address(sg); | ||
| 754 | tcd->soff = 0; | ||
| 755 | tcd->doff = 4; | ||
| 756 | } else { | ||
| 757 | tcd->saddr = sg_dma_address(sg); | ||
| 758 | tcd->daddr = per_paddr; | ||
| 759 | tcd->soff = 4; | ||
| 760 | tcd->doff = 0; | ||
| 761 | } | ||
| 762 | |||
| 763 | tcd->ssize = MPC_DMA_TSIZE_4; | ||
| 764 | tcd->dsize = MPC_DMA_TSIZE_4; | ||
| 765 | |||
| 766 | len = sg_dma_len(sg); | ||
| 767 | tcd->nbytes = tcd_nunits * 4; | ||
| 768 | if (!IS_ALIGNED(len, tcd->nbytes)) | ||
| 769 | goto err_prep; | ||
| 770 | |||
| 771 | iter = len / tcd->nbytes; | ||
| 772 | if (iter >= 1 << 15) { | ||
| 773 | /* len is too big */ | ||
| 774 | goto err_prep; | ||
| 775 | } | ||
| 776 | /* citer_linkch contains the high bits of iter */ | ||
| 777 | tcd->biter = iter & 0x1ff; | ||
| 778 | tcd->biter_linkch = iter >> 9; | ||
| 779 | tcd->citer = tcd->biter; | ||
| 780 | tcd->citer_linkch = tcd->biter_linkch; | ||
| 781 | |||
| 782 | tcd->e_sg = 0; | ||
| 783 | tcd->d_req = 1; | ||
| 784 | |||
| 785 | /* Place descriptor in prepared list */ | ||
| 786 | spin_lock_irqsave(&mchan->lock, iflags); | ||
| 787 | list_add_tail(&mdesc->node, &mchan->prepared); | ||
| 788 | spin_unlock_irqrestore(&mchan->lock, iflags); | ||
| 789 | } | ||
| 790 | |||
| 791 | return &mdesc->desc; | ||
| 792 | |||
| 793 | err_prep: | ||
| 794 | /* Put the descriptor back */ | ||
| 795 | spin_lock_irqsave(&mchan->lock, iflags); | ||
| 796 | list_add_tail(&mdesc->node, &mchan->free); | ||
| 797 | spin_unlock_irqrestore(&mchan->lock, iflags); | ||
| 798 | |||
| 799 | return NULL; | ||
| 800 | } | ||
| 801 | |||
| 802 | static int mpc_dma_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | ||
| 803 | unsigned long arg) | ||
| 804 | { | ||
| 805 | struct mpc_dma_chan *mchan; | ||
| 806 | struct mpc_dma *mdma; | ||
| 807 | struct dma_slave_config *cfg; | ||
| 808 | unsigned long flags; | ||
| 809 | |||
| 810 | mchan = dma_chan_to_mpc_dma_chan(chan); | ||
| 811 | switch (cmd) { | ||
| 812 | case DMA_TERMINATE_ALL: | ||
| 813 | /* Disable channel requests */ | ||
| 814 | mdma = dma_chan_to_mpc_dma(chan); | ||
| 815 | |||
| 816 | spin_lock_irqsave(&mchan->lock, flags); | ||
| 817 | |||
| 818 | out_8(&mdma->regs->dmacerq, chan->chan_id); | ||
| 819 | list_splice_tail_init(&mchan->prepared, &mchan->free); | ||
| 820 | list_splice_tail_init(&mchan->queued, &mchan->free); | ||
| 821 | list_splice_tail_init(&mchan->active, &mchan->free); | ||
| 822 | |||
| 823 | spin_unlock_irqrestore(&mchan->lock, flags); | ||
| 824 | |||
| 825 | return 0; | ||
| 826 | |||
| 827 | case DMA_SLAVE_CONFIG: | ||
| 828 | /* | ||
| 829 | * Software constraints: | ||
| 830 | * - only transfers between a peripheral device and | ||
| 831 | * memory are supported; | ||
| 832 | * - only peripheral devices with 4-byte FIFO access register | ||
| 833 | * are supported; | ||
| 834 | * - minimal transfer chunk is 4 bytes and consequently | ||
| 835 | * source and destination addresses must be 4-byte aligned | ||
| 836 | * and transfer size must be aligned on (4 * maxburst) | ||
| 837 | * boundary; | ||
| 838 | * - during the transfer RAM address is being incremented by | ||
| 839 | * the size of minimal transfer chunk; | ||
| 840 | * - peripheral port's address is constant during the transfer. | ||
| 841 | */ | ||
| 842 | |||
| 843 | cfg = (void *)arg; | ||
| 844 | |||
| 845 | if (cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES || | ||
| 846 | cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES || | ||
| 847 | !IS_ALIGNED(cfg->src_addr, 4) || | ||
| 848 | !IS_ALIGNED(cfg->dst_addr, 4)) { | ||
| 849 | return -EINVAL; | ||
| 850 | } | ||
| 851 | |||
| 852 | spin_lock_irqsave(&mchan->lock, flags); | ||
| 853 | |||
| 854 | mchan->src_per_paddr = cfg->src_addr; | ||
| 855 | mchan->src_tcd_nunits = cfg->src_maxburst; | ||
| 856 | mchan->dst_per_paddr = cfg->dst_addr; | ||
| 857 | mchan->dst_tcd_nunits = cfg->dst_maxburst; | ||
| 858 | |||
| 859 | /* Apply defaults */ | ||
| 860 | if (mchan->src_tcd_nunits == 0) | ||
| 861 | mchan->src_tcd_nunits = 1; | ||
| 862 | if (mchan->dst_tcd_nunits == 0) | ||
| 863 | mchan->dst_tcd_nunits = 1; | ||
| 864 | |||
| 865 | spin_unlock_irqrestore(&mchan->lock, flags); | ||
| 866 | |||
| 867 | return 0; | ||
| 868 | |||
| 869 | default: | ||
| 870 | /* Unknown command */ | ||
| 871 | break; | ||
| 872 | } | ||
| 873 | |||
| 874 | return -ENXIO; | ||
| 875 | } | ||
| 876 | |||
| 646 | static int mpc_dma_probe(struct platform_device *op) | 877 | static int mpc_dma_probe(struct platform_device *op) |
| 647 | { | 878 | { |
| 648 | struct device_node *dn = op->dev.of_node; | 879 | struct device_node *dn = op->dev.of_node; |
| @@ -733,9 +964,12 @@ static int mpc_dma_probe(struct platform_device *op) | |||
| 733 | dma->device_issue_pending = mpc_dma_issue_pending; | 964 | dma->device_issue_pending = mpc_dma_issue_pending; |
| 734 | dma->device_tx_status = mpc_dma_tx_status; | 965 | dma->device_tx_status = mpc_dma_tx_status; |
| 735 | dma->device_prep_dma_memcpy = mpc_dma_prep_memcpy; | 966 | dma->device_prep_dma_memcpy = mpc_dma_prep_memcpy; |
| 967 | dma->device_prep_slave_sg = mpc_dma_prep_slave_sg; | ||
| 968 | dma->device_control = mpc_dma_device_control; | ||
| 736 | 969 | ||
| 737 | INIT_LIST_HEAD(&dma->channels); | 970 | INIT_LIST_HEAD(&dma->channels); |
| 738 | dma_cap_set(DMA_MEMCPY, dma->cap_mask); | 971 | dma_cap_set(DMA_MEMCPY, dma->cap_mask); |
| 972 | dma_cap_set(DMA_SLAVE, dma->cap_mask); | ||
| 739 | 973 | ||
| 740 | for (i = 0; i < dma->chancnt; i++) { | 974 | for (i = 0; i < dma->chancnt; i++) { |
| 741 | mchan = &mdma->channels[i]; | 975 | mchan = &mdma->channels[i]; |
