Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: sprd: Support DMA link-list mode

The Spreadtrum DMA can support the link-list transaction mode, which means
DMA controller can do transaction one by one automatically once we linked
these transaction by link-list register.

Signed-off-by: Eric Long <eric.long@spreadtrum.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Eric Long and committed by
Vinod Koul
4ac69546 5b394b2d

+143 -7
+74 -7
drivers/dma/sprd-dma.c
··· 68 68 69 69 /* SPRD_DMA_CHN_CFG register definition */ 70 70 #define SPRD_DMA_CHN_EN BIT(0) 71 + #define SPRD_DMA_LINKLIST_EN BIT(4) 71 72 #define SPRD_DMA_WAIT_BDONE_OFFSET 24 72 73 #define SPRD_DMA_DONOT_WAIT_BDONE 1 73 74 ··· 104 103 #define SPRD_DMA_REQ_MODE_MASK GENMASK(1, 0) 105 104 #define SPRD_DMA_FIX_SEL_OFFSET 21 106 105 #define SPRD_DMA_FIX_EN_OFFSET 20 107 - #define SPRD_DMA_LLIST_END_OFFSET 19 106 + #define SPRD_DMA_LLIST_END BIT(19) 108 107 #define SPRD_DMA_FRG_LEN_MASK GENMASK(16, 0) 109 108 110 109 /* SPRD_DMA_CHN_BLK_LEN register definition */ ··· 165 164 struct sprd_dma_chn { 166 165 struct virt_dma_chan vc; 167 166 void __iomem *chn_base; 167 + struct sprd_dma_linklist linklist; 168 168 struct dma_slave_config slave_cfg; 169 169 u32 chn_num; 170 170 u32 dev_id; ··· 584 582 } 585 583 586 584 static int sprd_dma_fill_desc(struct dma_chan *chan, 587 - struct sprd_dma_desc *sdesc, 585 + struct sprd_dma_chn_hw *hw, 586 + unsigned int sglen, int sg_index, 588 587 dma_addr_t src, dma_addr_t dst, u32 len, 589 588 enum dma_transfer_direction dir, 590 589 unsigned long flags, ··· 593 590 { 594 591 struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan); 595 592 struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); 596 - struct sprd_dma_chn_hw *hw = &sdesc->chn_hw; 597 593 u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK; 598 594 u32 int_mode = flags & SPRD_DMA_INT_MASK; 599 595 int src_datawidth, dst_datawidth, src_step, dst_step; ··· 672 670 temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET; 673 671 hw->trsf_step = temp; 674 672 673 + /* link-list configuration */ 674 + if (schan->linklist.phy_addr) { 675 + if (sg_index == sglen - 1) 676 + hw->frg_len |= SPRD_DMA_LLIST_END; 677 + 678 + hw->cfg |= SPRD_DMA_LINKLIST_EN; 679 + 680 + /* link-list index */ 681 + temp = (sg_index + 1) % sglen; 682 + /* Next link-list configuration's physical address offset */ 683 + temp = temp * sizeof(*hw) + SPRD_DMA_CHN_SRC_ADDR; 684 + /* 685 + * Set the link-list pointer point to next link-list 686 + * configuration's physical address. 687 + */ 688 + hw->llist_ptr = schan->linklist.phy_addr + temp; 689 + } else { 690 + hw->llist_ptr = 0; 691 + } 692 + 675 693 hw->frg_step = 0; 676 694 hw->src_blk_step = 0; 677 695 hw->des_blk_step = 0; 678 696 return 0; 697 + } 698 + 699 + static int sprd_dma_fill_linklist_desc(struct dma_chan *chan, 700 + unsigned int sglen, int sg_index, 701 + dma_addr_t src, dma_addr_t dst, u32 len, 702 + enum dma_transfer_direction dir, 703 + unsigned long flags, 704 + struct dma_slave_config *slave_cfg) 705 + { 706 + struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); 707 + struct sprd_dma_chn_hw *hw; 708 + 709 + if (!schan->linklist.virt_addr) 710 + return -EINVAL; 711 + 712 + hw = (struct sprd_dma_chn_hw *)(schan->linklist.virt_addr + 713 + sg_index * sizeof(*hw)); 714 + 715 + return sprd_dma_fill_desc(chan, hw, sglen, sg_index, src, dst, len, 716 + dir, flags, slave_cfg); 679 717 } 680 718 681 719 static struct dma_async_tx_descriptor * ··· 786 744 u32 len = 0; 787 745 int ret, i; 788 746 789 - /* TODO: now we only support one sg for each DMA configuration. */ 790 - if (!is_slave_direction(dir) || sglen > 1) 747 + if (!is_slave_direction(dir)) 791 748 return NULL; 749 + 750 + if (context) { 751 + struct sprd_dma_linklist *ll_cfg = 752 + (struct sprd_dma_linklist *)context; 753 + 754 + schan->linklist.phy_addr = ll_cfg->phy_addr; 755 + schan->linklist.virt_addr = ll_cfg->virt_addr; 756 + } else { 757 + schan->linklist.phy_addr = 0; 758 + schan->linklist.virt_addr = 0; 759 + } 792 760 793 761 sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT); 794 762 if (!sdesc) ··· 814 762 src = slave_cfg->src_addr; 815 763 dst = sg_dma_address(sg); 816 764 } 765 + 766 + /* 767 + * The link-list mode needs at least 2 link-list 768 + * configurations. If there is only one sg, it doesn't 769 + * need to fill the link-list configuration. 770 + */ 771 + if (sglen < 2) 772 + break; 773 + 774 + ret = sprd_dma_fill_linklist_desc(chan, sglen, i, src, dst, len, 775 + dir, flags, slave_cfg); 776 + if (ret) { 777 + kfree(sdesc); 778 + return NULL; 779 + } 817 780 } 818 781 819 - ret = sprd_dma_fill_desc(chan, sdesc, src, dst, len, dir, flags, 820 - slave_cfg); 782 + ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len, 783 + dir, flags, slave_cfg); 821 784 if (ret) { 822 785 kfree(sdesc); 823 786 return NULL;
+69
include/linux/dma/sprd-dma.h
··· 58 58 SPRD_DMA_CFGERR_INT, 59 59 }; 60 60 61 + /* 62 + * struct sprd_dma_linklist - DMA link-list address structure 63 + * @virt_addr: link-list virtual address to configure link-list node 64 + * @phy_addr: link-list physical address to link DMA transfer 65 + * 66 + * The Spreadtrum DMA controller supports the link-list mode, that means slaves 67 + * can supply several groups configurations (each configuration represents one 68 + * DMA transfer) saved in memory, and DMA controller will link these groups 69 + * configurations by writing the physical address of each configuration into the 70 + * link-list register. 71 + * 72 + * Just as shown below, the link-list pointer register will be pointed to the 73 + * physical address of 'configuration 1', and the 'configuration 1' link-list 74 + * pointer will be pointed to 'configuration 2', and so on. 75 + * Once trigger the DMA transfer, the DMA controller will load 'configuration 76 + * 1' to its registers automatically, after 'configuration 1' transaction is 77 + * done, DMA controller will load 'configuration 2' automatically, until all 78 + * DMA transactions are done. 79 + * 80 + * Note: The last link-list pointer should point to the physical address 81 + * of 'configuration 1', which can avoid DMA controller loads incorrect 82 + * configuration when the last configuration transaction is done. 83 + * 84 + * DMA controller linklist memory 85 + * ====================== ----------------------- 86 + *| | | configuration 1 |<--- 87 + *| DMA controller | ------->| | | 88 + *| | | | | | 89 + *| | | | | | 90 + *| | | | | | 91 + *| linklist pointer reg |---- ----| linklist pointer | | 92 + * ====================== | ----------------------- | 93 + * | | 94 + * | ----------------------- | 95 + * | | configuration 2 | | 96 + * --->| | | 97 + * | | | 98 + * | | | 99 + * | | | 100 + * ----| linklist pointer | | 101 + * | ----------------------- | 102 + * | | 103 + * | ----------------------- | 104 + * | | configuration 3 | | 105 + * --->| | | 106 + * | | | 107 + * | . | | 108 + * . | 109 + * . | 110 + * . | 111 + * | . | 112 + * | ----------------------- | 113 + * | | configuration n | | 114 + * --->| | | 115 + * | | | 116 + * | | | 117 + * | | | 118 + * | linklist pointer |---- 119 + * ----------------------- 120 + * 121 + * To support the link-list mode, DMA slaves should allocate one segment memory 122 + * from always-on IRAM or dma coherent memory to store these groups of DMA 123 + * configuration, and pass the virtual and physical address to DMA controller. 124 + */ 125 + struct sprd_dma_linklist { 126 + unsigned long virt_addr; 127 + phys_addr_t phy_addr; 128 + }; 129 + 61 130 #endif