diff options
-rw-r--r-- | arch/arm/mach-exynos/dma.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-spear13xx/spear13xx.c | 3 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap700x.c | 3 | ||||
-rw-r--r-- | drivers/dma/Kconfig | 17 | ||||
-rw-r--r-- | drivers/dma/Makefile | 2 | ||||
-rw-r--r-- | drivers/dma/amba-pl08x.c | 2 | ||||
-rw-r--r-- | drivers/dma/dw_dmac.c | 258 | ||||
-rw-r--r-- | drivers/dma/dw_dmac_regs.h | 48 | ||||
-rw-r--r-- | drivers/dma/edma.c | 671 | ||||
-rw-r--r-- | drivers/dma/ioat/dma_v2.c | 3 | ||||
-rw-r--r-- | drivers/dma/ioat/pci.c | 22 | ||||
-rw-r--r-- | drivers/dma/mmp_pdma.c | 875 | ||||
-rw-r--r-- | drivers/dma/mmp_tdma.c | 51 | ||||
-rw-r--r-- | drivers/dma/mxs-dma.c | 14 | ||||
-rw-r--r-- | drivers/dma/pl330.c | 78 | ||||
-rw-r--r-- | drivers/dma/sirf-dma.c | 23 | ||||
-rw-r--r-- | drivers/dma/ste_dma40.c | 14 | ||||
-rw-r--r-- | drivers/dma/tegra20-apb-dma.c | 12 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/spi/spi-davinci.c | 292 | ||||
-rw-r--r-- | include/linux/dw_dmac.h | 7 | ||||
-rw-r--r-- | include/linux/edma.h | 29 | ||||
-rw-r--r-- | include/linux/platform_data/mmp_dma.h | 19 |
23 files changed, 2085 insertions, 361 deletions
diff --git a/arch/arm/mach-exynos/dma.c b/arch/arm/mach-exynos/dma.c index f60b66dbcf8..21d568b3b14 100644 --- a/arch/arm/mach-exynos/dma.c +++ b/arch/arm/mach-exynos/dma.c @@ -303,10 +303,12 @@ static int __init exynos_dma_init(void) dma_cap_set(DMA_SLAVE, exynos_pdma0_pdata.cap_mask); dma_cap_set(DMA_CYCLIC, exynos_pdma0_pdata.cap_mask); + dma_cap_set(DMA_PRIVATE, exynos_pdma0_pdata.cap_mask); amba_device_register(&exynos_pdma0_device, &iomem_resource); dma_cap_set(DMA_SLAVE, exynos_pdma1_pdata.cap_mask); dma_cap_set(DMA_CYCLIC, exynos_pdma1_pdata.cap_mask); + dma_cap_set(DMA_PRIVATE, exynos_pdma1_pdata.cap_mask); amba_device_register(&exynos_pdma1_device, &iomem_resource); dma_cap_set(DMA_MEMCPY, exynos_mdma1_pdata.cap_mask); diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c index e10648801b2..5633d698f1e 100644 --- a/arch/arm/mach-spear13xx/spear13xx.c +++ b/arch/arm/mach-spear13xx/spear13xx.c @@ -78,6 +78,9 @@ struct dw_dma_platform_data dmac_plat_data = { .nr_channels = 8, .chan_allocation_order = CHAN_ALLOCATION_DESCENDING, .chan_priority = CHAN_PRIORITY_DESCENDING, + .block_size = 4095U, + .nr_masters = 2, + .data_width = { 3, 3, 0, 0 }, }; void __init spear13xx_l2x0_init(void) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 0445c4fd67e..b323d8d3185 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -605,6 +605,9 @@ static void __init genclk_init_parent(struct clk *clk) static struct dw_dma_platform_data dw_dmac0_data = { .nr_channels = 3, + .block_size = 4095U, + .nr_masters = 2, + .data_width = { 2, 2, 0, 0 }, }; static struct resource dw_dmac0_resource[] = { diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d06ea2950dd..677cd6e4e1a 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -208,6 +208,16 @@ config SIRF_DMA help Enable support for the CSR SiRFprimaII DMA engine. +config TI_EDMA + tristate "TI EDMA support" + depends on ARCH_DAVINCI + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + default n + help + Enable support for the TI EDMA controller. This DMA + engine is found on TI DaVinci and AM33xx parts. + config ARCH_HAS_ASYNC_TX_FIND_CHANNEL bool @@ -292,6 +302,13 @@ config DMA_OMAP select DMA_ENGINE select DMA_VIRTUAL_CHANNELS +config MMP_PDMA + bool "MMP PDMA support" + depends on (ARCH_MMP || ARCH_PXA) + select DMA_ENGINE + help + Support the MMP PDMA engine for PXA and MMP platfrom. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 4cf6b128ab9..7428feaa870 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_IMX_DMA) += imx-dma.o obj-$(CONFIG_MXS_DMA) += mxs-dma.o obj-$(CONFIG_TIMB_DMA) += timb_dma.o obj-$(CONFIG_SIRF_DMA) += sirf-dma.o +obj-$(CONFIG_TI_EDMA) += edma.o obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o obj-$(CONFIG_PL330_DMA) += pl330.o @@ -32,3 +33,4 @@ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o obj-$(CONFIG_DMA_OMAP) += omap-dma.o +obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6fbeebb9486..d1cc5791476 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1892,6 +1892,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->pd = dev_get_platdata(&adev->dev); if (!pl08x->pd) { dev_err(&adev->dev, "no platform data supplied\n"); + ret = -EINVAL; goto out_no_platdata; } @@ -1943,6 +1944,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) dev_err(&adev->dev, "%s failed to allocate " "physical channel holders\n", __func__); + ret = -ENOMEM; goto out_no_phychans; } diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d3c5a5a88f1..c4b0eb3cde8 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -36,12 +36,22 @@ * which does not support descriptor writeback. */ +static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) +{ + return slave ? slave->dst_master : 0; +} + +static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) +{ + return slave ? slave->src_master : 1; +} + #define DWC_DEFAULT_CTLLO(_chan) ({ \ struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ - int _dms = __slave ? __slave->dst_master : 0; \ - int _sms = __slave ? __slave->src_master : 1; \ + int _dms = dwc_get_dms(__slave); \ + int _sms = dwc_get_sms(__slave); \ u8 _smsize = __slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ u8 _dmsize = __slave ? _sconfig->dst_maxburst : \ @@ -56,16 +66,6 @@ }) /* - * This is configuration-dependent and usually a funny size like 4095. - * - * Note that this is a transfer count, i.e. if we transfer 32-bit - * words, we can do 16380 bytes per descriptor. - * - * This parameter is also system-specific. - */ -#define DWC_MAX_COUNT 4095U - -/* * Number of descriptors to allocate for each channel. This should be * made configurable somehow; preferably, the clients (at least the * ones using slave transfers) should be able to give us a hint. @@ -177,6 +177,11 @@ static void dwc_initialize(struct dw_dma_chan *dwc) cfghi = dws->cfg_hi; cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; + } else { + if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV) + cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id); + else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM) + cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id); } channel_writel(dwc, CFG_LO, cfglo); @@ -206,7 +211,7 @@ static inline unsigned int dwc_fast_fls(unsigned long long v) return 0; } -static void dwc_dump_chan_regs(struct dw_dma_chan *dwc) +static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) { dev_err(chan2dev(&dwc->chan), " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", @@ -227,10 +232,29 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) /*----------------------------------------------------------------------*/ +/* Perform single block transfer */ +static inline void dwc_do_single_block(struct dw_dma_chan *dwc, + struct dw_desc *desc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + u32 ctllo; + + /* Software emulation of LLP mode relies on interrupts to continue + * multi block transfer. */ + ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN; + + channel_writel(dwc, SAR, desc->lli.sar); + channel_writel(dwc, DAR, desc->lli.dar); + channel_writel(dwc, CTL_LO, ctllo); + channel_writel(dwc, CTL_HI, desc->lli.ctlhi); + channel_set_bit(dw, CH_EN, dwc->mask); +} + /* Called with dwc->lock held and bh disabled */ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long was_soft_llp; /* ASSERT: channel is idle */ if (dma_readl(dw, CH_EN) & dwc->mask) { @@ -242,6 +266,26 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) return; } + if (dwc->nollp) { + was_soft_llp = test_and_set_bit(DW_DMA_IS_SOFT_LLP, + &dwc->flags); + if (was_soft_llp) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start new LLP transfer " + "inside ongoing one\n"); + return; + } + + dwc_initialize(dwc); + + dwc->tx_list = &first->tx_list; + dwc->tx_node_active = first->tx_list.next; + + dwc_do_single_block(dwc, first); + + return; + } + dwc_initialize(dwc); channel_writel(dwc, LLP, first->txd.phys); @@ -553,8 +597,36 @@ static void dw_dma_tasklet(unsigned long data) dwc_handle_cyclic(dw, dwc, status_err, status_xfer); else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); - else if (status_xfer & (1 << i)) + else if (status_xfer & (1 << i)) { + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + if (dwc->tx_node_active != dwc->tx_list) { + struct dw_desc *desc = + list_entry(dwc->tx_node_active, + struct dw_desc, + desc_node); + + dma_writel(dw, CLEAR.XFER, dwc->mask); + + /* move pointer to next descriptor */ + dwc->tx_node_active = + dwc->tx_node_active->next; + + dwc_do_single_block(dwc, desc); + + spin_unlock_irqrestore(&dwc->lock, flags); + continue; + } else { + /* we are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + } + } + spin_unlock_irqrestore(&dwc->lock, flags); + dwc_scan_descriptors(dw, dwc); + } } /* @@ -636,6 +708,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_slave *dws = chan->private; struct dw_desc *desc; struct dw_desc *first; struct dw_desc *prev; @@ -643,6 +716,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t offset; unsigned int src_width; unsigned int dst_width; + unsigned int data_width; u32 ctllo; dev_vdbg(chan2dev(chan), @@ -655,7 +729,11 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } - src_width = dst_width = dwc_fast_fls(src | dest | len); + data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], + dwc->dw->data_width[dwc_get_dms(dws)]); + + src_width = dst_width = min_t(unsigned int, data_width, + dwc_fast_fls(src | dest | len)); ctllo = DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_DST_WIDTH(dst_width) @@ -667,7 +745,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, for (offset = 0; offset < len; offset += xfer_count << src_width) { xfer_count = min_t(size_t, (len - offset) >> src_width, - DWC_MAX_COUNT); + dwc->block_size); desc = dwc_desc_get(dwc); if (!desc) @@ -725,6 +803,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dma_addr_t reg; unsigned int reg_width; unsigned int mem_width; + unsigned int data_width; unsigned int i; struct scatterlist *sg; size_t total_len = 0; @@ -748,6 +827,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); + data_width = dwc->dw->data_width[dwc_get_sms(dws)]; + for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len, dlen, mem; @@ -755,7 +836,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem = sg_dma_address(sg); len = sg_dma_len(sg); - mem_width = dwc_fast_fls(mem | len); + mem_width = min_t(unsigned int, + data_width, dwc_fast_fls(mem | len)); slave_sg_todev_fill_desc: desc = dwc_desc_get(dwc); @@ -768,8 +850,8 @@ slave_sg_todev_fill_desc: desc->lli.sar = mem; desc->lli.dar = reg; desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); - if ((len >> mem_width) > DWC_MAX_COUNT) { - dlen = DWC_MAX_COUNT << mem_width; + if ((len >> mem_width) > dwc->block_size) { + dlen = dwc->block_size << mem_width; mem += dlen; len -= dlen; } else { @@ -808,6 +890,8 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); + data_width = dwc->dw->data_width[dwc_get_dms(dws)]; + for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len, dlen, mem; @@ -815,7 +899,8 @@ slave_sg_todev_fill_desc: mem = sg_dma_address(sg); len = sg_dma_len(sg); - mem_width = dwc_fast_fls(mem | len); + mem_width = min_t(unsigned int, + data_width, dwc_fast_fls(mem | len)); slave_sg_fromdev_fill_desc: desc = dwc_desc_get(dwc); @@ -828,8 +913,8 @@ slave_sg_fromdev_fill_desc: desc->lli.sar = reg; desc->lli.dar = mem; desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); - if ((len >> reg_width) > DWC_MAX_COUNT) { - dlen = DWC_MAX_COUNT << reg_width; + if ((len >> reg_width) > dwc->block_size) { + dlen = dwc->block_size << reg_width; mem += dlen; len -= dlen; } else { @@ -945,6 +1030,8 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, } else if (cmd == DMA_TERMINATE_ALL) { spin_lock_irqsave(&dwc->lock, flags); + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + dwc_chan_disable(dw, dwc); dwc->paused = false; @@ -1187,6 +1274,13 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->nollp) { + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(chan2dev(&dwc->chan), + "channel doesn't support LLP transfers\n"); + return ERR_PTR(-EINVAL); + } + if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { spin_unlock_irqrestore(&dwc->lock, flags); dev_dbg(chan2dev(&dwc->chan), @@ -1212,7 +1306,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, periods = buf_len / period_len; /* Check for too big/unaligned periods and unaligned DMA buffer. */ - if (period_len > (DWC_MAX_COUNT << reg_width)) + if (period_len > (dwc->block_size << reg_width)) goto out_err; if (unlikely(period_len & ((1 << reg_width) - 1))) goto out_err; @@ -1374,6 +1468,11 @@ static int __devinit dw_probe(struct platform_device *pdev) struct resource *io; struct dw_dma *dw; size_t size; + void __iomem *regs; + bool autocfg; + unsigned int dw_params; + unsigned int nr_channels; + unsigned int max_blk_size = 0; int irq; int err; int i; @@ -1390,32 +1489,46 @@ static int __devinit dw_probe(struct platform_device *pdev) if (irq < 0) return irq; - size = sizeof(struct dw_dma); - size += pdata->nr_channels * sizeof(struct dw_dma_chan); - dw = kzalloc(size, GFP_KERNEL); + regs = devm_request_and_ioremap(&pdev->dev, io); + if (!regs) + return -EBUSY; + + dw_params = dma_read_byaddr(regs, DW_PARAMS); + autocfg = dw_params >> DW_PARAMS_EN & 0x1; + + if (autocfg) + nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; + else + nr_channels = pdata->nr_channels; + + size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); + dw = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); if (!dw) return -ENOMEM; - if (!request_mem_region(io->start, DW_REGLEN, pdev->dev.driver->name)) { - err = -EBUSY; - goto err_kfree; - } + dw->clk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dw->clk)) + return PTR_ERR(dw->clk); + clk_prepare_enable(dw->clk); - dw->regs = ioremap(io->start, DW_REGLEN); - if (!dw->regs) { - err = -ENOMEM; - goto err_release_r; - } + dw->regs = regs; + + /* get hardware configuration parameters */ + if (autocfg) { + max_blk_size = dma_readl(dw, MAX_BLK_SIZE); - dw->clk = clk_get(&pdev->dev, "hclk"); - if (IS_ERR(dw->clk)) { - err = PTR_ERR(dw->clk); - goto err_clk; + dw->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; + for (i = 0; i < dw->nr_masters; i++) { + dw->data_width[i] = + (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; + } + } else { + dw->nr_masters = pdata->nr_masters; + memcpy(dw->data_width, pdata->data_width, 4); } - clk_prepare_enable(dw->clk); /* Calculate all channel mask before DMA setup */ - dw->all_chan_mask = (1 << pdata->nr_channels) - 1; + dw->all_chan_mask = (1 << nr_channels) - 1; /* force dma off, just in case */ dw_dma_off(dw); @@ -1423,17 +1536,19 @@ static int __devinit dw_probe(struct platform_device *pdev) /* disable BLOCK interrupts as well */ channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); - err = request_irq(irq, dw_dma_interrupt, 0, "dw_dmac", dw); + err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0, + "dw_dmac", dw); if (err) - goto err_irq; + return err; platform_set_drvdata(pdev, dw); tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); INIT_LIST_HEAD(&dw->dma.channels); - for (i = 0; i < pdata->nr_channels; i++) { + for (i = 0; i < nr_channels; i++) { struct dw_dma_chan *dwc = &dw->chan[i]; + int r = nr_channels - i - 1; dwc->chan.device = &dw->dma; dma_cookie_init(&dwc->chan); @@ -1445,7 +1560,7 @@ static int __devinit dw_probe(struct platform_device *pdev) /* 7 is highest priority & 0 is lowest. */ if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) - dwc->priority = pdata->nr_channels - i - 1; + dwc->priority = r; else dwc->priority = i; @@ -1458,6 +1573,32 @@ static int __devinit dw_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dwc->free_list); channel_clear_bit(dw, CH_EN, dwc->mask); + + dwc->dw = dw; + + /* hardware configuration */ + if (autocfg) { + unsigned int dwc_params; + + dwc_params = dma_read_byaddr(regs + r * sizeof(u32), + DWC_PARAMS); + + /* Decode maximum block size for given channel. The + * stored 4 bit value represents blocks from 0x00 for 3 + * up to 0x0a for 4095. */ + dwc->block_size = + (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1; + dwc->nollp = + (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; + } else { + dwc->block_size = pdata->block_size; + + /* Check if channel supports multi block transfer */ + channel_writel(dwc, LLP, 0xfffffffc); + dwc->nollp = + (channel_readl(dwc, LLP) & 0xfffffffc) == 0; + channel_writel(dwc, LLP, 0); + } } /* Clear all interrupts on all channels. */ @@ -1486,35 +1627,21 @@ static int __devinit dw_probe(struct platform_device *pdev) dma_writel(dw, CFG, DW_CFG_DMA_EN); printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", - dev_name(&pdev->dev), pdata->nr_channels); + dev_name(&pdev->dev), nr_channels); dma_async_device_register(&dw->dma); return 0; - -err_irq: - clk_disable_unprepare(dw->clk); - clk_put(dw->clk); -err_clk: - iounmap(dw->regs); - dw->regs = NULL; -err_release_r: - release_resource(io); -err_kfree: - kfree(dw); - return err; } static int __devexit dw_remove(struct platform_device *pdev) { struct dw_dma *dw = platform_get_drvdata(pdev); struct dw_dma_chan *dwc, *_dwc; - struct resource *io; dw_dma_off(dw); dma_async_device_unregister(&dw->dma); - free_irq(platform_get_irq(pdev, 0), dw); tasklet_kill(&dw->tasklet); list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, @@ -1523,17 +1650,6 @@ static int __devexit dw_remove(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); } - clk_disable_unprepare(dw->clk); - clk_put(dw->clk); - - iounmap(dw->regs); - dw->regs = NULL; - - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(io->start, DW_REGLEN); - - kfree(dw); - return 0; } diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 50830bee087..ff39fa6cd2b 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -82,9 +82,39 @@ struct dw_dma_regs { DW_REG(ID); DW_REG(TEST); + /* reserved */ + DW_REG(__reserved0); + DW_REG(__reserved1); + /* optional encoded params, 0x3c8..0x3f7 */ + u32 __reserved; + + /* per-channel configuration registers */ + u32 DWC_PARAMS[DW_DMA_MAX_NR_CHANNELS]; + u32 MULTI_BLK_TYPE; + u32 MAX_BLK_SIZE; + + /* top-level parameters */ + u32 DW_PARAMS; }; +/* To access the registers in early stage of probe */ +#define dma_read_byaddr(addr, name) \ + readl((addr) + offsetof(struct dw_dma_regs, name)) + +/* Bitfields in DW_PARAMS */ +#define DW_PARAMS_NR_CHAN 8 /* number of channels */ +#define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ +#define DW_PARAMS_DATA_WIDTH(n) (15 + 2 * (n)) +#define DW_PARAMS_DATA_WIDTH1 15 /* master 1 data width */ +#define DW_PARAMS_DATA_WIDTH2 17 /* master 2 data width */ +#define DW_PARAMS_DATA_WIDTH3 19 /* master 3 data width */ +#define DW_PARAMS_DATA_WIDTH4 21 /* master 4 data width */ +#define DW_PARAMS_EN 28 /* encoded parameters */ + +/* Bitfields in DWC_PARAMS */ +#define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ + /* Bitfields in CTL_LO */ #define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ #define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ @@ -140,10 +170,9 @@ struct dw_dma_regs { /* Bitfields in CFG */ #define DW_CFG_DMA_EN (1 << 0) -#define DW_REGLEN 0x400 - enum dw_dmac_flags { DW_DMA_IS_CYCLIC = 0, + DW_DMA_IS_SOFT_LLP = 1, }; struct dw_dma_chan { @@ -154,6 +183,10 @@ struct dw_dma_chan { bool paused; bool initialized; + /* software emulation of the LLP transfers */ + struct list_head *tx_list; + struct list_head *tx_node_active; + spinlock_t lock; /* these other elements are all protected by lock */ @@ -165,8 +198,15 @@ struct dw_dma_chan { unsigned int descs_allocated; + /* hardware configuration */ + unsigned int block_size; + bool nollp; + /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; + + /* backlink to dw_dma */ + struct dw_dma *dw; }; static inline struct dw_dma_chan_regs __iomem * @@ -193,6 +233,10 @@ struct dw_dma { u8 all_chan_mask; + /* hardware configuration */ + unsigned char nr_masters; + unsigned char data_width[4]; + struct dw_dma_chan chan[0]; }; diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c new file mode 100644 index 00000000000..05aea3ce850 --- /dev/null +++ b/drivers/dma/edma.c @@ -0,0 +1,671 @@ +/* + * TI EDMA DMA engine driver + * + * Copyright 2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <mach/edma.h> + +#include "dmaengine.h" +#include "virt-dma.h" + +/* + * This will go away when the private EDMA API is folded + * into this driver and the platform device(s) are + * instantiated in the arch code. We can only get away + * with this simplification because DA8XX may not be built + * in the same kernel image with other DaVinci parts. This + * avoids having to sprinkle dmaengine driver platform devices + * and data throughout all the existing board files. + */ +#ifdef CONFIG_ARCH_DAVINCI_DA8XX +#define EDMA_CTLRS 2 +#define EDMA_CHANS 32 +#else +#define EDMA_CTLRS 1 +#define EDMA_CHANS 64 +#endif /* CONFIG_ARCH_DAVINCI_DA8XX */ + +/* Max of 16 segments per channel to conserve PaRAM slots */ +#define MAX_NR_SG 16 +#define EDMA_MAX_SLOTS MAX_NR_SG +#define EDMA_DESCRIPTORS 16 + +struct edma_desc { + struct virt_dma_desc vdesc; + struct list_head node; + int absync; + int pset_nr; + struct edmacc_param pset[0]; +}; + +struct edma_cc; + +struct edma_chan { + struct virt_dma_chan vchan; + struct list_head node; + struct edma_desc *edesc; + struct edma_cc *ecc; + int ch_num; + bool alloced; + int slot[EDMA_MAX_SLOTS]; + dma_addr_t addr; + int addr_width; + int maxburst; +}; + +struct edma_cc { + int ctlr; + struct dma_device dma_slave; + struct edma_chan slave_chans[EDMA_CHANS]; + int num_slave_chans; + int dummy_slot; +}; + +static inline struct edma_cc *to_edma_cc(struct dma_device *d) +{ + return container_of(d, struct edma_cc, dma_slave); +} + +static inline struct edma_chan *to_edma_chan(struct dma_chan *c) +{ + return container_of(c, struct edma_chan, vchan.chan); +} + +static inline struct edma_desc +*to_edma_desc(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct edma_desc, vdesc.tx); +} + +static void edma_desc_free(struct virt_dma_desc *vdesc) +{ + kfree(container_of(vdesc, struct edma_desc, vdesc)); +} + +/* Dispatch a queued descriptor to the controller (caller holds lock) */ +static void edma_execute(struct edma_chan *echan) +{ + struct virt_dma_desc *vdesc = vchan_next_desc(&echan->vchan); + struct edma_desc *edesc; + int i; + + if (!vdesc) { + echan->edesc = NULL; + return; + } + + list_del(&vdesc->node); + + echan->edesc = edesc = to_edma_desc(&vdesc->tx); + + /* Write descriptor PaRAM set(s) */ + for (i = 0; i < edesc->pset_nr; i++) { + edma_write_slot(echan->slot[i], &edesc->pset[i]); + dev_dbg(echan->vchan.chan.device->dev, + "\n pset[%d]:\n" + " chnum\t%d\n" + " slot\t%d\n" + " opt\t%08x\n" + " src\t%08x\n" + " dst\t%08x\n" + " abcnt\t%08x\n" + " ccnt\t%08x\n" + " bidx\t%08x\n" + " cidx\t%08x\n" + " lkrld\t%08x\n", + i, echan->ch_num, echan->slot[i], + edesc->pset[i].opt, + edesc->pset[i].src, + edesc->pset[i].dst, + edesc->pset[i].a_b_cnt, + edesc->pset[i].ccnt, + edesc->pset[i].src_dst_bidx, + edesc->pset[i].src_dst_cidx, + edesc->pset[i].link_bcntrld); + /* Link to the previous slot if not the last set */ + if (i != (edesc->pset_nr - 1)) + edma_link(echan->slot[i], echan->slot[i+1]); + /* Final pset links to the dummy pset */ + else + edma_link(echan->slot[i], echan->ecc->dummy_slot); + } + + edma_start(echan->ch_num); +} + +static int edma_terminate_all(struct edma_chan *echan) +{ + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&echan->vchan.lock, flags); + + /* + * Stop DMA activity: we assume the callback will not be called + * after edma_dma() returns (even if it does, it will see + * echan->edesc is NULL and exit.) + */ + if (echan->edesc) { + echan->edesc = NULL; + edma_stop(echan->ch_num); + } + + vchan_get_all_descriptors(&echan->vchan, &head); + spin_unlock_irqrestore(&echan->vchan.lock, flags); + vchan_dma_desc_free_list(&echan->vchan, &head); + + return 0; +} + + +static int edma_slave_config(struct edma_chan *echan, + struct dma_slave_config *config) +{ + if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) || + (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) + return -EINVAL; + + if (config->direction == DMA_MEM_TO_DEV) { + if (config->dst_addr) + echan->addr = config->dst_addr; + if (config->dst_addr_width) + echan->addr_width = config->dst_addr_width; + if (config->dst_maxburst) + echan->maxburst = config->dst_maxburst; + } else if (config->direction == DMA_DEV_TO_MEM) { + if (config->src_addr) + echan->addr = config->src_addr; + if (config->src_addr_width) + echan->addr_width = config->src_addr_width; + if (config->src_maxburst) + echan->maxburst = config->src_maxburst; + } + + return 0; +} + +static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + int ret = 0; + struct dma_slave_config *config; + struct edma_chan *echan = to_edma_chan(chan); + + switch (cmd) { + case DMA_TERMINATE_ALL: + edma_terminate_all(echan); + break; + case DMA_SLAVE_CONFIG: + config = (struct dma_slave_config *)arg; + ret = edma_slave_config(echan, config); + break; + default: + ret = -ENOSYS; + } + + return ret; +} + +static struct dma_async_tx_descriptor *edma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long tx_flags, void *context) +{ + struct edma_chan *echan = to_edma_chan(chan); + struct device *dev = chan->device->dev; + struct edma_desc *edesc; + struct scatterlist *sg; + int i; + int acnt, bcnt, ccnt, src, dst, cidx; + int src_bidx, dst_bidx, src_cidx, dst_cidx; + + if (unlikely(!echan || !sgl || !sg_len)) + return NULL; |