From 817ba283acf2d7b5aa073b96fd989f336fcff72a Mon Sep 17 00:00:00 2001 From: Wolfgang Wiedmeyer Date: Fri, 23 Oct 2015 05:50:33 +0200 Subject: merge more stuff from 3.2.72 --- drivers/dma/Kconfig | 15 +- drivers/dma/Makefile | 1 + drivers/dma/TODO | 1 - drivers/dma/amba-pl08x.c | 857 ++++++++++++++++++++++---------------------- drivers/dma/at_hdmac.c | 168 +++++++-- drivers/dma/at_hdmac_regs.h | 24 ++ drivers/dma/coh901318.c | 20 +- drivers/dma/dmaengine.c | 13 +- drivers/dma/dmatest.c | 24 +- drivers/dma/dw_dmac.c | 5 +- drivers/dma/imx-dma.c | 5 +- drivers/dma/imx-sdma.c | 141 ++++++-- drivers/dma/intel_mid_dma.c | 12 +- drivers/dma/ioat/dma.c | 62 +++- drivers/dma/ioat/dma.h | 7 +- drivers/dma/ioat/dma_v2.c | 19 +- drivers/dma/ioat/dma_v3.c | 19 +- drivers/dma/ioat/pci.c | 11 + drivers/dma/ipu/ipu_idmac.c | 73 ++-- drivers/dma/ipu/ipu_irq.c | 48 +-- drivers/dma/mpc512x_dma.c | 1 - drivers/dma/mv_xor.c | 84 +++-- drivers/dma/mv_xor.h | 1 + drivers/dma/mxs-dma.c | 58 +-- drivers/dma/pch_dma.c | 5 +- drivers/dma/pl330.c | 298 ++++++++++++--- drivers/dma/shdma.c | 211 +++++++---- drivers/dma/shdma.h | 11 + drivers/dma/ste_dma40.c | 318 +++++++++------- drivers/dma/ste_dma40_ll.h | 3 - drivers/dma/timb_dma.c | 5 +- 31 files changed, 1586 insertions(+), 934 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6a718b7..a842317 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -124,7 +124,7 @@ config MV_XOR config MX3_IPU bool "MX3x Image Processing Unit support" - depends on ARCH_MX3 + depends on SOC_IMX31 || SOC_IMX35 select DMA_ENGINE default y help @@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL config PL330_DMA tristate "DMA API Driver for PL330" select DMA_ENGINE - depends on PL330 + depends on ARM_AMBA + select PL330 help Select if your platform has one or more PL330 DMACs. You need to provide platform specific settings via @@ -214,7 +215,7 @@ config PCH_DMA config IMX_SDMA tristate "i.MX SDMA support" - depends on ARCH_MX25 || ARCH_MX3 || ARCH_MX5 + depends on ARCH_MX25 || SOC_IMX31 || SOC_IMX35 || ARCH_MX5 select DMA_ENGINE help Support the i.MX SDMA engine. This engine is integrated into @@ -236,6 +237,13 @@ config MXS_DMA Support the MXS DMA engine. This engine including APBH-DMA and APBX-DMA is integrated into Freescale i.MX23/28 chips. +config EP93XX_DMA + bool "Cirrus Logic EP93xx DMA support" + depends on ARCH_EP93XX + select DMA_ENGINE + help + Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller. + config DMA_ENGINE bool @@ -246,6 +254,7 @@ config NET_DMA bool "Network: TCP receive copy offload" depends on DMA_ENGINE && NET default (INTEL_IOATDMA || FSL_DMA) + depends on BROKEN help This enables the use of DMA engines in the network stack to offload receive copy-to-user operations, freeing CPU cycles. diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 836095a..30cf3b1 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o +obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o diff --git a/drivers/dma/TODO b/drivers/dma/TODO index a4af858..734ed02 100644 --- a/drivers/dma/TODO +++ b/drivers/dma/TODO @@ -9,6 +9,5 @@ TODO for slave dma - mxs-dma.c - dw_dmac - intel_mid_dma - - ste_dma40 4. Check other subsystems for dma drivers and merge/move to dmaengine 5. Remove dma_slave_config's dma direction. diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index e6d7228..b7cbd1a 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -66,31 +66,29 @@ * after the final transfer signalled by LBREQ or LSREQ. The DMAC * will then move to the next LLI entry. * - * Only the former works sanely with scatter lists, so we only implement - * the DMAC flow control method. However, peripherals which use the LBREQ - * and LSREQ signals (eg, MMCI) are unable to use this mode, which through - * these hardware restrictions prevents them from using scatter DMA. - * * Global TODO: * - Break out common code from arch/arm/mach-s3c64xx and share */ -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - +#include #include #define DRIVER_NAME "pl08xdmac" +static struct amba_driver pl08x_amba_driver; + /** * struct vendor_data - vendor-specific config parameters for PL08x derivatives * @channels: the number of channels available in this variant @@ -125,7 +123,8 @@ struct pl08x_lli { * @phy_chans: array of data for the physical channels * @pool: a pool for the LLI descriptors * @pool_ctr: counter of LLIs in the pool - * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI fetches + * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI + * fetches * @mem_buses: set to indicate memory transfers on AHB2. * @lock: a spinlock for this struct */ @@ -148,22 +147,10 @@ struct pl08x_driver_data { * PL08X specific defines */ -/* - * Memory boundaries: the manual for PL08x says that the controller - * cannot read past a 1KiB boundary, so these defines are used to - * create transfer LLIs that do not cross such boundaries. - */ -#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */ -#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT) - -/* Minimum period between work queue runs */ -#define PL08X_WQ_PERIODMIN 20 - /* Size (bytes) of each LLI buffer allocated for one transfer */ # define PL08X_LLI_TSFR_SIZE 0x2000 /* Maximum times we call dma_pool_alloc on this pool without freeing */ -#define PL08X_MAX_ALLOCS 0x40 #define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli)) #define PL08X_ALIGN 8 @@ -275,7 +262,6 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch) writel(val, ch->base + PL080_CH_CONFIG); } - /* * pl08x_terminate_phy_chan() stops the channel, clears the FIFO and * clears any pending interrupt status. This should not be used for @@ -366,7 +352,9 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) if (!list_empty(&plchan->pend_list)) { struct pl08x_txd *txdi; list_for_each_entry(txdi, &plchan->pend_list, node) { - bytes += txdi->len; + struct pl08x_sg *dsg; + list_for_each_entry(dsg, &txd->dsg_list, node) + bytes += dsg->len; } } @@ -410,6 +398,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, return NULL; } + pm_runtime_get_sync(&pl08x->adev->dev); return ch; } @@ -423,6 +412,8 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, /* Stop the channel and clear its interrupts */ pl08x_terminate_phy_chan(pl08x, ch); + pm_runtime_put(&pl08x->adev->dev); + /* Mark it as free */ ch->serving = NULL; spin_unlock_irqrestore(&ch->lock, flags); @@ -495,43 +486,37 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth, struct pl08x_lli_build_data { struct pl08x_txd *txd; - struct pl08x_driver_data *pl08x; struct pl08x_bus_data srcbus; struct pl08x_bus_data dstbus; size_t remainder; + u32 lli_bus; }; /* - * Autoselect a master bus to use for the transfer this prefers the - * destination bus if both available if fixed address on one bus the - * other will be chosen + * Autoselect a master bus to use for the transfer. Slave will be the chosen as + * victim in case src & dest are not similarly aligned. i.e. If after aligning + * masters address with width requirements of transfer (by sending few byte by + * byte data), slave is still not aligned, then its width will be reduced to + * BYTE. + * - prefers the destination bus if both available + * - prefers bus with fixed address (i.e. peripheral) */ static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd, struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus, u32 cctl) { if (!(cctl & PL080_CONTROL_DST_INCR)) { - *mbus = &bd->srcbus; - *sbus = &bd->dstbus; - } else if (!(cctl & PL080_CONTROL_SRC_INCR)) { *mbus = &bd->dstbus; *sbus = &bd->srcbus; + } else if (!(cctl & PL080_CONTROL_SRC_INCR)) { + *mbus = &bd->srcbus; + *sbus = &bd->dstbus; } else { - if (bd->dstbus.buswidth == 4) { - *mbus = &bd->dstbus; - *sbus = &bd->srcbus; - } else if (bd->srcbus.buswidth == 4) { - *mbus = &bd->srcbus; - *sbus = &bd->dstbus; - } else if (bd->dstbus.buswidth == 2) { + if (bd->dstbus.buswidth >= bd->srcbus.buswidth) { *mbus = &bd->dstbus; *sbus = &bd->srcbus; - } else if (bd->srcbus.buswidth == 2) { + } else { *mbus = &bd->srcbus; *sbus = &bd->dstbus; - } else { - /* bd->srcbus.buswidth == 1 */ - *mbus = &bd->dstbus; - *sbus = &bd->srcbus; } } } @@ -550,9 +535,9 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd, llis_va[num_llis].cctl = cctl; llis_va[num_llis].src = bd->srcbus.addr; llis_va[num_llis].dst = bd->dstbus.addr; - llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli); - if (bd->pl08x->lli_buses & PL08X_AHB2) - llis_va[num_llis].lli |= PL080_LLI_LM_AHB2; + llis_va[num_llis].lli = llis_bus + (num_llis + 1) * + sizeof(struct pl08x_lli); + llis_va[num_llis].lli |= bd->lli_bus; if (cctl & PL080_CONTROL_SRC_INCR) bd->srcbus.addr += len; @@ -564,16 +549,12 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd, bd->remainder -= len; } -/* - * Return number of bytes to fill to boundary, or len. - * This calculation works for any value of addr. - */ -static inline size_t pl08x_pre_boundary(u32 addr, size_t len) +static inline void prep_byte_width_lli(struct pl08x_lli_build_data *bd, + u32 *cctl, u32 len, int num_llis, size_t *total_bytes) { - size_t boundary_len = PL08X_BOUNDARY_SIZE - - (addr & (PL08X_BOUNDARY_SIZE - 1)); - - return min(boundary_len, len); + *cctl = pl08x_cctl_bits(*cctl, 1, 1, len); + pl08x_fill_lli_for_desc(bd, num_llis, len, *cctl); + (*total_bytes) += len; } /* @@ -587,13 +568,12 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_bus_data *mbus, *sbus; struct pl08x_lli_build_data bd; int num_llis = 0; - u32 cctl; - size_t max_bytes_per_lli; - size_t total_bytes = 0; + u32 cctl, early_bytes = 0; + size_t max_bytes_per_lli, total_bytes; struct pl08x_lli *llis_va; + struct pl08x_sg *dsg; - txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, - &txd->llis_bus); + txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus); if (!txd->llis_va) { dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__); return 0; @@ -601,13 +581,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, pl08x->pool_ctr++; - /* Get the default CCTL */ - cctl = txd->cctl; - bd.txd = txd; - bd.pl08x = pl08x; - bd.srcbus.addr = txd->src_addr; - bd.dstbus.addr = txd->dst_addr; + bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0; + cctl = txd->cctl; /* Find maximum width of the source bus */ bd.srcbus.maxwidth = @@ -619,215 +595,179 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_DWIDTH_MASK) >> PL080_CONTROL_DWIDTH_SHIFT); - /* Set up the bus widths to the maximum */ - bd.srcbus.buswidth = bd.srcbus.maxwidth; - bd.dstbus.buswidth = bd.dstbus.maxwidth; - dev_vdbg(&pl08x->adev->dev, - "%s source bus is %d bytes wide, dest bus is %d bytes wide\n", - __func__, bd.srcbus.buswidth, bd.dstbus.buswidth); + list_for_each_entry(dsg, &txd->dsg_list, node) { + total_bytes = 0; + cctl = txd->cctl; + bd.srcbus.addr = dsg->src_addr; + bd.dstbus.addr = dsg->dst_addr; + bd.remainder = dsg->len; + bd.srcbus.buswidth = bd.srcbus.maxwidth; + bd.dstbus.buswidth = bd.dstbus.maxwidth; - /* - * Bytes transferred == tsize * MIN(buswidths), not max(buswidths) - */ - max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) * - PL080_CONTROL_TRANSFER_SIZE_MASK; - dev_vdbg(&pl08x->adev->dev, - "%s max bytes per lli = %zu\n", - __func__, max_bytes_per_lli); + pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); - /* We need to count this down to zero */ - bd.remainder = txd->len; - dev_vdbg(&pl08x->adev->dev, - "%s remainder = %zu\n", - __func__, bd.remainder); + dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n", + bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "", + bd.srcbus.buswidth, + bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "", + bd.dstbus.buswidth, + bd.remainder); + dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n", + mbus == &bd.srcbus ? "src" : "dst", + sbus == &bd.srcbus ? "src" : "dst"); - /* - * Choose bus to align to - * - prefers destination bus if both available - * - if fixed address on one bus chooses other - */ - pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); + /* + * Zero length is only allowed if all these requirements are + * met: + * - flow controller is peripheral. + * - src.addr is aligned to src.width + * - dst.addr is aligned to dst.width + * + * sg_len == 1 should be true, as there can be two cases here: + * + * - Memory addresses are contiguous and are not scattered. + * Here, Only one sg will be passed by user driver, with + * memory address and zero length. We pass this to controller + * and after the transfer it will receive the last burst + * request from peripheral and so transfer finishes. + * + * - Memory addresses are scattered and are not contiguous. + * Here, Obviously as DMA controller doesn't know when a lli's + * transfer gets over, it can't load next lli. So in this + * case, there has to be an assumption that only one lli is + * supported. Thus, we can't have scattered addresses. + */ + if (!bd.remainder) { + u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >> + PL080_CONFIG_FLOW_CONTROL_SHIFT; + if (!((fc >= PL080_FLOW_SRC2DST_DST) && + (fc <= PL080_FLOW_SRC2DST_SRC))) { + dev_err(&pl08x->adev->dev, "%s sg len can't be zero", + __func__); + return 0; + } - if (txd->len < mbus->buswidth) { - /* Less than a bus width available - send as single bytes */ - while (bd.remainder) { - dev_vdbg(&pl08x->adev->dev, - "%s single byte LLIs for a transfer of " - "less than a bus width (remain 0x%08x)\n", - __func__, bd.remainder); - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl); - total_bytes++; - } - } else { - /* Make one byte LLIs until master bus is aligned */ - while ((mbus->addr) % (mbus->buswidth)) { - dev_vdbg(&pl08x->adev->dev, - "%s adjustment lli for less than bus width " - "(remain 0x%08x)\n", - __func__, bd.remainder); - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl); - total_bytes++; + if ((bd.srcbus.addr % bd.srcbus.buswidth) || + (bd.srcbus.addr % bd.srcbus.buswidth)) { + dev_err(&pl08x->adev->dev, + "%s src & dst address must be aligned to src" + " & dst width if peripheral is flow controller", + __func__); + return 0; + } + + cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, + bd.dstbus.buswidth, 0); + pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl); + break; } /* - * Master now aligned - * - if slave is not then we must set its width down + * Send byte by byte for following cases + * - Less than a bus width available + * - until master bus is aligned */ - if (sbus->addr % sbus->buswidth) { - dev_dbg(&pl08x->adev->dev, - "%s set down bus width to one byte\n", - __func__); + if (bd.remainder < mbus->buswidth) + early_bytes = bd.remainder; + else if ((mbus->addr) % (mbus->buswidth)) { + early_bytes = mbus->buswidth - (mbus->addr) % + (mbus->buswidth); + if ((bd.remainder - early_bytes) < mbus->buswidth) + early_bytes = bd.remainder; + } - sbus->buswidth = 1; + if (early_bytes) { + dev_vdbg(&pl08x->adev->dev, + "%s byte width LLIs (remain 0x%08x)\n", + __func__, bd.remainder); + prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++, + &total_bytes); } - /* - * Make largest possible LLIs until less than one bus - * width left - */ - while (bd.remainder > (mbus->buswidth - 1)) { - size_t lli_len, target_len, tsize, odd_bytes; + if (bd.remainder) { + /* + * Master now aligned + * - if slave is not then we must set its width down + */ + if (sbus->addr % sbus->buswidth) { + dev_dbg(&pl08x->adev->dev, + "%s set down bus width to one byte\n", + __func__); + + sbus->buswidth = 1; + } /* - * If enough left try to send max possible, - * otherwise try to send the remainder + * Bytes transferred = tsize * src width, not + * MIN(buswidths) */ - target_len = min(bd.remainder, max_bytes_per_lli); + max_bytes_per_lli = bd.srcbus.buswidth * + PL080_CONTROL_TRANSFER_SIZE_MASK; + dev_vdbg(&pl08x->adev->dev, + "%s max bytes per lli = %zu\n", + __func__, max_bytes_per_lli); /* - * Set bus lengths for incrementing buses to the - * number of bytes which fill to next memory boundary, - * limiting on the target length calculated above. + * Make largest possible LLIs until less than one bus + * width left */ - if (cctl & PL080_CONTROL_SRC_INCR) - bd.srcbus.fill_bytes = - pl08x_pre_boundary(bd.srcbus.addr, - target_len); - else - bd.srcbus.fill_bytes = target_len; - - if (cctl & PL080_CONTROL_DST_INCR) - bd.dstbus.fill_bytes = - pl08x_pre_boundary(bd.dstbus.addr, - target_len); - else - bd.dstbus.fill_bytes = target_len; - - /* Find the nearest */ - lli_len = min(bd.srcbus.fill_bytes, - bd.dstbus.fill_bytes); - - BUG_ON(lli_len > bd.remainder); - - if (lli_len <= 0) { - dev_err(&pl08x->adev->dev, - "%s lli_len is %zu, <= 0\n", - __func__, lli_len); - return 0; - } + while (bd.remainder > (mbus->buswidth - 1)) { + size_t lli_len, tsize, width; - if (lli_len == target_len) { - /* - * Can send what we wanted. - * Maintain alignment - */ - lli_len = (lli_len/mbus->buswidth) * - mbus->buswidth; - odd_bytes = 0; - } else { /* - * So now we know how many bytes to transfer - * to get to the nearest boundary. The next - * LLI will past the boundary. However, we - * may be working to a boundary on the slave - * bus. We need to ensure the master stays - * aligned, and that we are working in - * multiples of the bus widths. + * If enough left try to send max possible, + * otherwise try to send the remainder */ - odd_bytes = lli_len % mbus->buswidth; - lli_len -= odd_bytes; + lli_len = min(bd.remainder, max_bytes_per_lli); - } - - if (lli_len) { /* - * Check against minimum bus alignment: - * Calculate actual transfer size in relation - * to bus width an get a maximum remainder of - * the smallest bus width - 1 + * Check against maximum bus alignment: + * Calculate actual transfer size in relation to + * bus width an get a maximum remainder of the + * highest bus width - 1 */ - /* FIXME: use round_down()? */ - tsize = lli_len / min(mbus->buswidth, - sbus->buswidth); - lli_len = tsize * min(mbus->buswidth, - sbus->buswidth); - - if (target_len != lli_len) { - dev_vdbg(&pl08x->adev->dev, - "%s can't send what we want. Desired 0x%08zx, lli of 0x%08zx bytes in txd of 0x%08zx\n", - __func__, target_len, lli_len, txd->len); - } - - cctl = pl08x_cctl_bits(cctl, - bd.srcbus.buswidth, - bd.dstbus.buswidth, - tsize); + width = max(mbus->buswidth, sbus->buswidth); + lli_len = (lli_len / width) * width; + tsize = lli_len / bd.srcbus.buswidth; dev_vdbg(&pl08x->adev->dev, - "%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)\n", + "%s fill lli with single lli chunk of " + "size 0x%08zx (remainder 0x%08zx)\n", __func__, lli_len, bd.remainder); + + cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, + bd.dstbus.buswidth, tsize); pl08x_fill_lli_for_desc(&bd, num_llis++, - lli_len, cctl); + lli_len, cctl); total_bytes += lli_len; } - - if (odd_bytes) { - /* - * Creep past the boundary, maintaining - * master alignment - */ - int j; - for (j = 0; (j < mbus->buswidth) - && (bd.remainder); j++) { - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - dev_vdbg(&pl08x->adev->dev, - "%s align with boundary, single byte (remain 0x%08zx)\n", - __func__, bd.remainder); - pl08x_fill_lli_for_desc(&bd, - num_llis++, 1, cctl); - total_bytes++; - } + /* + * Send any odd bytes + */ + if (bd.remainder) { + dev_vdbg(&pl08x->adev->dev, + "%s align with boundary, send odd bytes (remain %zu)\n", + __func__, bd.remainder); + prep_byte_width_lli(&bd, &cctl, bd.remainder, + num_llis++, &total_bytes); } } - /* - * Send any odd bytes - */ - while (bd.remainder) { - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - dev_vdbg(&pl08x->adev->dev, - "%s align with boundary, single odd byte (remain %zu)\n", - __func__, bd.remainder); - pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl); - total_bytes++; + if (total_bytes != dsg->len) { + dev_err(&pl08x->adev->dev, + "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n", + __func__, total_bytes, dsg->len); + return 0; } - } - if (total_bytes != txd->len) { - dev_err(&pl08x->adev->dev, - "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n", - __func__, total_bytes, txd->len); - return 0; - } - if (num_llis >= MAX_NUM_TSFR_LLIS) { - dev_err(&pl08x->adev->dev, - "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n", - __func__, (u32) MAX_NUM_TSFR_LLIS); - return 0; + if (num_llis >= MAX_NUM_TSFR_LLIS) { + dev_err(&pl08x->adev->dev, + "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n", + __func__, (u32) MAX_NUM_TSFR_LLIS); + return 0; + } } llis_va = txd->llis_va; @@ -840,15 +780,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, { int i; + dev_vdbg(&pl08x->adev->dev, + "%-3s %-9s %-10s %-10s %-10s %s\n", + "lli", "", "csrc", "cdst", "clli", "cctl"); for (i = 0; i < num_llis; i++) { dev_vdbg(&pl08x->adev->dev, - "lli %d @%p: csrc=0x%08x, cdst=0x%08x, cctl=0x%08x, clli=0x%08x\n", - i, - &llis_va[i], - llis_va[i].src, - llis_va[i].dst, - llis_va[i].cctl, - llis_va[i].lli + "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i, &llis_va[i], llis_va[i].src, + llis_va[i].dst, llis_va[i].lli, llis_va[i].cctl ); } } @@ -861,11 +800,19 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, static void pl08x_free_txd(struct pl08x_driver_data *pl08x, struct pl08x_txd *txd) { + struct pl08x_sg *dsg, *_dsg; + /* Free the LLI */ - dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus); + if (txd->llis_va) + dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus); pl08x->pool_ctr--; + list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) { + list_del(&dsg->node); + kfree(dsg); + } + kfree(txd); } @@ -922,9 +869,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, * need, but for slaves the physical signals may be muxed! * Can the platform allow us to use this channel? */ - if (plchan->slave && - ch->signal < 0 && - pl08x->pd->get_signal) { + if (plchan->slave && pl08x->pd->get_signal) { ret = pl08x->pd->get_signal(plchan); if (ret < 0) { dev_dbg(&pl08x->adev->dev, @@ -1013,10 +958,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt( * If slaves are relying on interrupts to signal completion this function * must not be called with interrupts disabled. */ -static enum dma_status -pl08x_dma_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, - struct dma_tx_state *txstate) +static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); dma_cookie_t last_used; @@ -1054,64 +997,105 @@ pl08x_dma_tx_status(struct dma_chan *chan, /* PrimeCell DMA extension */ struct burst_table { - int burstwords; + u32 burstwords; u32 reg; }; static const struct burst_table burst_sizes[] = { { .burstwords = 256, - .reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_256, }, { .burstwords = 128, - .reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_128, }, { .burstwords = 64, - .reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_64, }, { .burstwords = 32, - .reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_32, }, { .burstwords = 16, - .reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_16, }, { .burstwords = 8, - .reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_8, }, { .burstwords = 4, - .reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT), + .reg = PL080_BSIZE_4, }, { - .burstwords = 1, - .reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT), + .burstwords = 0, + .reg = PL080_BSIZE_1, }, }; +/* + * Given the source and destination available bus masks, select which + * will be routed to each port. We try to have source and destination + * on separate ports, but always respect the allowable settings. + */ +static u32 pl08x_select_bus(u8 src, u8 dst) +{ + u32 cctl = 0; + + if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1))) + cctl |= PL080_CONTROL_DST_AHB2; + if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2))) + cctl |= PL080_CONTROL_SRC_AHB2; + + return cctl; +} + +static u32 pl08x_cctl(u32 cctl) +{ + cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 | + PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR | + PL080_CONTROL_PROT_MASK); + + /* Access the cell in privileged mode, non-bufferable, non-cacheable */ + return cctl | PL080_CONTROL_PROT_SYS; +} + +static u32 pl08x_width(enum dma_slave_buswidth width) +{ + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + return PL080_WIDTH_8BIT; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + return PL080_WIDTH_16BIT; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return PL080_WIDTH_32BIT; + default: + return ~0; + } +} + +static u32 pl08x_burst(u32 maxburst) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(burst_sizes); i++) + if (burst_sizes[i].burstwords <= maxburst) + break; + + return burst_sizes[i].reg; +} + static int dma_set_runtime_config(struct dma_chan *chan, struct dma_slave_config *config) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; - struct pl08x_channel_data *cd = plchan->cd; enum dma_slave_buswidth addr_width; - dma_addr_t addr; - u32 maxburst; + u32 width, burst, maxburst; u32 cctl = 0; - int i; if (!plchan->slave) return -EINVAL; @@ -1119,11 +1103,9 @@ static int dma_set_runtime_config(struct dma_chan *chan, /* Transfer direction */ plchan->runtime_direction = config->direction; if (config->direction == DMA_TO_DEVICE) { - addr = config->dst_addr; addr_width = config->dst_addr_width; maxburst = config->dst_maxburst; } else if (config->direction == DMA_FROM_DEVICE) { - addr = config->src_addr; addr_width = config->src_addr_width; maxburst = config->src_maxburst; } else { @@ -1132,46 +1114,40 @@ static int dma_set_runtime_config(struct dma_chan *chan, return -EINVAL; } - switch (addr_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) | - (PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT); - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) | - (PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT); - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) | - (PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT); - break; - default: + width = pl08x_width(addr_width); + if (width == ~0) { dev_err(&pl08x->adev->dev, "bad runtime_config: alien address width\n"); return -EINVAL; } + cctl |= width << PL080_CONTROL_SWIDTH_SHIFT; + cctl |= width << PL080_CONTROL_DWIDTH_SHIFT; + /* - * Now decide on a maxburst: * If this channel will only request single transfers, set this * down to ONE element. Also select one element if no maxburst * is specified. */ - if (plchan->cd->single || maxburst == 0) { - cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) | - (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT); + if (plchan->cd->single) + maxburst = 1; + + burst = pl08x_burst(maxburst); + cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT; + cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT; + + if (plchan->runtime_direction == DMA_FROM_DEVICE) { + plchan->src_addr = config->src_addr; + plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR | + pl08x_select_bus(plchan->cd->periph_buses, + pl08x->mem_buses); } else { - for (i = 0; i < ARRAY_SIZE(burst_sizes); i++) - if (burst_sizes[i].burstwords <= maxburst) - break; - cctl |= burst_sizes[i].reg; + plchan->dst_addr = config->dst_addr; + plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR | + pl08x_select_bus(pl08x->mem_buses, + plchan->cd->periph_buses); } - plchan->runtime_addr = addr; - - /* Modify the default channel data to fit PrimeCell request */ - cd->cctl = cctl; - dev_dbg(&pl08x->adev->dev, "configured channel %s (%s) for %s, data width %d, " "maxburst %d words, LE, CCTL=0x%08x\n", @@ -1225,7 +1201,9 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, num_llis = pl08x_fill_llis_for_desc(pl08x, txd); if (!num_llis) { - kfree(txd); + spin_lock_irqsave(&plchan->lock, flags); + pl08x_free_txd(pl08x, txd); + spin_unlock_irqrestore(&plchan->lock, flags); return -EINVAL; } @@ -1270,33 +1248,17 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, return 0; } -/* - * Given the source and destination available bus masks, select which - * will be routed to each port. We try to have source and destination - * on separate ports, but always respect the allowable settings. - */ -static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst) -{ - u32 cctl = 0; - - if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1))) - cctl |= PL080_CONTROL_DST_AHB2; - if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2))) - cctl |= PL080_CONTROL_SRC_AHB2; - - return cctl; -} - static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan, unsigned long flags) { - struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT); + struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT); if (txd) { dma_async_tx_descriptor_init(&txd->tx, &plchan->chan); txd->tx.flags = flags; txd->tx.tx_submit = pl08x_tx_submit; INIT_LIST_HEAD(&txd->node); + INIT_LIST_HEAD(&txd->dsg_list); /* Always enable error and terminal interrupts */ txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK | @@ -1315,6 +1277,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_txd *txd; + struct pl08x_sg *dsg; int ret; txd = pl08x_get_txd(plchan, flags); @@ -1324,10 +1287,19 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( return NULL; } + dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT); + if (!dsg) { + pl08x_free_txd(pl08x, txd); + dev_err(&pl08x->adev->dev, "%s no memory for pl080 sg\n", + __func__); + return NULL; + } + list_add_tail(&dsg->node, &txd->dsg_list); + txd->direction = DMA_NONE; - txd->src_addr = src; - txd->dst_addr = dest; - txd->len = len; + dsg->src_addr = src; + dsg->dst_addr = dest; + dsg->len = len; /* Set platform data for m2m */ txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; @@ -1338,8 +1310,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR; if (pl08x->vd->dualmaster) - txd->cctl |= pl08x_select_bus(pl08x, - pl08x->mem_buses, pl08x->mem_buses); + txd->cctl |= pl08x_select_bus(pl08x->mem_buses, + pl08x->mem_buses); ret = pl08x_prep_channel_resources(plchan, txd); if (ret) @@ -1356,20 +1328,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_txd *txd; - u8 src_buses, dst_buses; - int ret; - - /* - * Current implementation ASSUMES only one sg - */ - if (sg_len != 1) { - dev_err(&pl08x->adev->dev, "%s prepared too long sglist\n", - __func__); - BUG(); - } + struct pl08x_sg *dsg; + struct scatterlist *sg; + dma_addr_t slave_addr; + int ret, tmp; dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n", - __func__, sgl->length, plchan->name); + __func__, sgl->length, plchan->name); txd = pl08x_get_txd(plchan, flags); if (!txd) { @@ -1388,43 +1353,48 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( * channel target address dynamically at runtime. */ txd->direction = direction; - txd->len = sgl->length; - - txd->cctl = plchan->cd->cctl & - ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 | - PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR | - PL080_CONTROL_PROT_MASK); - - /* Access the cell in privileged mode, non-bufferable, non-cacheable */ - txd->cctl |= PL080_CONTROL_PROT_SYS; if (direction == DMA_TO_DEVICE) { - txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT; - txd->cctl |= PL080_CONTROL_SRC_INCR; - txd->src_addr = sgl->dma_address; - if (plchan->runtime_addr) - txd->dst_addr = plchan->runtime_addr; - else - txd->dst_addr = plchan->cd->addr; - src_buses = pl08x->mem_buses; - dst_buses = plchan->cd->periph_buses; + txd->cctl = plchan->dst_cctl; + slave_addr = plchan->dst_addr; } else if (direction == DMA_FROM_DEVICE) { - txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; - txd->cctl |= PL080_CONTROL_DST_INCR; - if (plchan->runtime_addr) - txd->src_addr = plchan->runtime_addr; - else - txd->src_addr = plchan->cd->addr; - txd->dst_addr = sgl->dma_address; - src_buses = plchan->cd->periph_buses; - dst_buses = pl08x->mem_buses; + txd->cctl = plchan->src_cctl; + slave_addr = plchan->src_addr; } else { + pl08x_free_txd(pl08x, txd); dev_err(&pl08x->adev->dev, "%s direction unsupported\n", __func__); return NULL; } - txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses); + if (plchan->cd->device_fc) + tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER_PER : + PL080_FLOW_PER2MEM_PER; + else + tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER : + PL080_FLOW_PER2MEM; + + txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT; + + for_each_sg(sgl, sg, sg_len, tmp) { + dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT); + if (!dsg) { + pl08x_free_txd(pl08x, txd); + dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n", + __func__); + return NULL; + } + list_add_tail(&dsg->node, &txd->dsg_list); + + dsg->len = sg_dma_len(sg); + if (direction == DMA_TO_DEVICE) { + dsg->src_addr = sg_phys(sg); + dsg->dst_addr = slave_addr; + } else { + dsg->src_addr = slave_addr; + dsg->dst_addr = sg_phys(sg); + } + } ret = pl08x_prep_channel_resources(plchan, txd); if (ret) @@ -1499,9 +1469,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, bool pl08x_filter_id(struct dma_chan *chan, void *chan_id) { - struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + struct pl08x_dma_chan *plchan; char *name = chan_id; + /* Reject channels for devices not bound to this driver */ + if (chan->device->dev->driver != &pl08x_amba_driver.drv) + return false; + + plchan = to_pl08x_chan(chan); + /* Check that the channel is not taken! */ if (!strcmp(plchan->name, name)) return true; @@ -1517,34 +1493,34 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id) */ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x) { - u32 val; - - val = readl(pl08x->base + PL080_CONFIG); - val &= ~(PL080_CONFIG_M2_BE | PL080_CONFIG_M1_BE | PL080_CONFIG_ENABLE); - /* We implicitly clear bit 1 and that means little-endian mode */ - val |= PL080_CONFIG_ENABLE; - writel(val, pl08x->base + PL080_CONFIG); + writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG); } static void pl08x_unmap_buffers(struct pl08x_txd *txd) { struct device *dev = txd->tx.chan->device->dev; + struct pl08x_sg *dsg; if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) - dma_unmap_single(dev, txd->src_addr, txd->len, - DMA_TO_DEVICE); - else - dma_unmap_page(dev, txd->src_addr, txd->len, - DMA_TO_DEVICE); + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_single(dev, dsg->src_addr, dsg->len, + DMA_TO_DEVICE); + else { + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_page(dev, dsg->src_addr, dsg->len, + DMA_TO_DEVICE); + } } if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) - dma_unmap_single(dev, txd->dst_addr, txd->len, - DMA_FROM_DEVICE); + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_single(dev, dsg->dst_addr, dsg->len, + DMA_FROM_DEVICE); else - dma_unmap_page(dev, txd->dst_addr, txd->len, - DMA_FROM_DEVICE); + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_page(dev, dsg->dst_addr, dsg->len, + DMA_FROM_DEVICE); } } @@ -1599,8 +1575,8 @@ static void pl08x_tasklet(unsigned long data) */ list_for_each_entry(waiting, &pl08x->memcpy.channels, chan.device_node) { - if (waiting->state == PL08X_CHAN_WAITING && - waiting->waiting != NULL) { + if (waiting->state == PL08X_CHAN_WAITING && + waiting->waiting != NULL) { int ret; /* This should REALLY not fail now */ @@ -1640,50 +1616,64 @@ static void pl08x_tasklet(unsigned long data) static irqreturn_t pl08x_irq(int irq, void *dev) { struct pl08x_driver_data *pl08x = dev; - u32 mask = 0; - u32 val; - int i; - - val = readl(pl08x->base + PL080_ERR_STATUS); - if (val) { - /* An error interrupt (on one or more channels) */ - dev_err(&pl08x->adev->dev, - "%s error interrupt, register value 0x%08x\n", - __func__, val); - /* - * Simply clear ALL PL08X error interrupts, - * regardless of channel and cause - * FIXME: should be 0x00000003 on PL081 really. - */ - writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR); + u32 mask = 0, err, tc, i; + + /* check & clear - ERR & TC interrupts */ + err = readl(pl08x->base + PL080_ERR_STATUS); + if (err) { + dev_err(&pl08x->adev->dev, "%s error interrupt, register value 0x%08x\n", + __func__, err); + writel(err, pl08x->base + PL080_ERR_CLEAR); } - val = readl(pl08x->base + PL080_INT_STATUS); + tc = readl(pl08x->base + PL080_INT_STATUS); + if (tc) + writel(tc, pl08x->base + PL080_TC_CLEAR); + + if (!err && !tc) + return IRQ_NONE; + for (i = 0; i < pl08x->vd->channels; i++) { - if ((1 << i) & val) { + if (((1 << i) & err) || ((1 << i) & tc)) { /* Locate physical channel */ struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i]; struct pl08x_dma_chan *plchan = phychan->serving; + if (!plchan) { + dev_err(&pl08x->adev->dev, + "%s Error TC interrupt on unused channel: 0x%08x\n", + __func__, i); + continue; + } + /* Schedule tasklet on this channel */ tasklet_schedule(&plchan->tasklet); - mask |= (1 << i); } } - /* Clear only the terminal interrupts on channels we processed */ - writel(mask, pl08x->base + PL080_TC_CLEAR); return mask ? IRQ_HANDLED : IRQ_NONE; } +static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan) +{ + u32 cctl = pl08x_cctl(chan->cd->cctl); + + chan->slave = true; + chan->name = chan->cd->bus_id; + chan->src_addr = chan->cd->addr; + chan->dst_addr = chan->cd->addr; + chan->src_cctl = cctl | PL080_CONTROL_DST_INCR | + pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses); + chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR | + pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses); +} + /* * Initialise the DMAC memcpy/slave channels. * Make a local wrapper to hold required data */ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, - struct dma_device *dmadev, - unsigned int channels, - bool slave) + struct dma_device *dmadev, unsigned int channels, bool slave) { struct pl08x_dma_chan *chan; int i; @@ -1696,7 +1686,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, * to cope with that situation. */ for (i = 0; i < channels; i++) { - chan = kzalloc(sizeof(struct pl08x_dma_chan), GFP_KERNEL); + chan = kzalloc(sizeof(*chan), GFP_KERNEL); if (!chan) { dev_err(&pl08x->adev->dev, "%s no memory for channel\n", __func__); @@ -1707,9 +1697,8 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, chan->state = PL08X_CHAN_IDLE; if (slave) { - chan->slave = true; - chan->name = pl08x->pd->slave_channels[i].bus_id; chan->cd = &pl08x->pd->slave_channels[i]; + pl08x_dma_slave_init(chan); } else { chan->cd = &pl08x->pd->memcpy_channel; chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i); @@ -1725,7 +1714,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, kfree(chan); continue; } - dev_info(&pl08x->adev->dev, + dev_dbg(&pl08x->adev->dev, "initialize virtual channel \"%s\"\n", chan->name); @@ -1834,9 +1823,9 @@ static const struct file_operations pl08x_debugfs_operations = { static void init_pl08x_debugfs(struct pl08x_driver_data *pl08x) { /* Expose a simple debugfs interface to view all clocks */ - (void) debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO, - NULL, pl08x, - &pl08x_debugfs_operations); + (void) debugfs_create_file(dev_name(&pl08x->adev->dev), + S_IFREG | S_IRUGO, NULL, pl08x, + &pl08x_debugfs_operations); } #else @@ -1857,12 +1846,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) return ret; /* Create the driver state holder */ - pl08x = kzalloc(sizeof(struct pl08x_driver_data), GFP_KERNEL); + pl08x = kzalloc(sizeof(*pl08x), GFP_KERNEL); if (!pl08x) { ret = -ENOMEM; goto out_no_pl08x; } + pm_runtime_set_active(&adev->dev); + pm_runtime_enable(&adev->dev); + /* Initialize memcpy engine */ dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask); pl08x->memcpy.dev = &adev->dev; @@ -1936,7 +1928,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) } /* Initialize physical channels */ - pl08x->phy_chans = kmalloc((vd->channels * sizeof(struct pl08x_phy_chan)), + pl08x->phy_chans = kmalloc((vd->channels * sizeof(*pl08x->phy_chans)), GFP_KERNEL); if (!pl08x->phy_chans) { dev_err(&adev->dev, "%s failed to allocate " @@ -1953,9 +1945,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&ch->lock); ch->serving = NULL; ch->signal = -1; - dev_info(&adev->dev, - "physical channel %d is %s\n", i, - pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE"); + dev_dbg(&adev->dev, "physical channel %d is %s\n", + i, pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE"); } /* Register as many memcpy channels as there are physical channels */ @@ -1971,8 +1962,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) /* Register slave channels */ ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->slave, - pl08x->pd->num_slave_channels, - true); + pl08x->pd->num_slave_channels, true); if (ret <= 0) { dev_warn(&pl08x->adev->dev, "%s failed to enumerate slave channels - %d\n", @@ -2002,6 +1992,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&pl08x->adev->dev, "DMA: PL%03x rev%u at 0x%08llx irq %d\n", amba_part(adev), amba_rev(adev), (unsigned long long)adev->res.start, adev->irq[0]); + + pm_runtime_put(&adev->dev); return 0; out_no_slave_reg: @@ -2020,6 +2012,9 @@ out_no_ioremap: dma_pool_destroy(pl08x->pool); out_no_lli_pool: out_no_platdata: + pm_runtime_put(&adev->dev); + pm_runtime_disable(&adev->dev); + kfree(pl08x); out_no_pl08x: amba_release_regions(adev); diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 1357c3b..c60d9c1 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -107,10 +107,11 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) { struct at_desc *desc, *_desc; struct at_desc *ret = NULL; + unsigned long flags; unsigned int i = 0; LIST_HEAD(tmp_list); - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) { i++; if (async_tx_test_ack(&desc->txd)) { @@ -121,7 +122,7 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) dev_dbg(chan2dev(&atchan->chan_common), "desc %p not ACKed\n", desc); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); dev_vdbg(chan2dev(&atchan->chan_common), "scanned %u descriptors on freelist\n", i); @@ -129,9 +130,9 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) if (!ret) { ret = atc_alloc_descriptor(&atchan->chan_common, GFP_ATOMIC); if (ret) { - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated++; - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else { dev_err(chan2dev(&atchan->chan_common), "not enough descriptors available\n"); @@ -150,8 +151,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) { if (desc) { struct at_desc *child; + unsigned long flags; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&atchan->chan_common), "moving child desc %p to freelist\n", @@ -160,7 +162,7 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) dev_vdbg(chan2dev(&atchan->chan_common), "moving desc %p to freelist\n", desc); list_add(&desc->desc_node, &atchan->free_list); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } } @@ -295,7 +297,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) /* for cyclic transfers, * no need to replay callback function while stopping */ - if (!test_bit(ATC_IS_CYCLIC, &atchan->status)) { + if (!atc_chan_is_cyclic(atchan)) { dma_async_tx_callback callback = txd->callback; void *param = txd->callback_param; @@ -467,16 +469,17 @@ static void atc_handle_cyclic(struct at_dma_chan *atchan) static void atc_tasklet(unsigned long data) { struct at_dma_chan *atchan = (struct at_dma_chan *)data; + unsigned long flags; - spin_lock(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status)) atc_handle_error(atchan); - else if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + else if (atc_chan_is_cyclic(atchan)) atc_handle_cyclic(atchan); else atc_advance_work(atchan); - spin_unlock(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } static irqreturn_t at_dma_interrupt(int irq, void *dev_id) @@ -535,8 +538,9 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) struct at_desc *desc = txd_to_at_desc(tx); struct at_dma_chan *atchan = to_at_dma_chan(tx->chan); dma_cookie_t cookie; + unsigned long flags; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); cookie = atc_assign_cookie(atchan, desc); if (list_empty(&atchan->active_list)) { @@ -550,7 +554,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) list_add_tail(&desc->desc_node, &atchan->queue); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); return cookie; } @@ -934,28 +938,29 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma *atdma = to_at_dma(chan->device); int chan_id = atchan->chan_common.chan_id; + unsigned long flags; LIST_HEAD(list); dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd); if (cmd == DMA_PAUSE) { - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id)); set_bit(ATC_IS_PAUSED, &atchan->status); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else if (cmd == DMA_RESUME) { - if (!test_bit(ATC_IS_PAUSED, &atchan->status)) + if (!atc_chan_is_paused(atchan)) return 0; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); dma_writel(atdma, CHDR, AT_DMA_RES(chan_id)); clear_bit(ATC_IS_PAUSED, &atchan->status); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else if (cmd == DMA_TERMINATE_ALL) { struct at_desc *desc, *_desc; /* @@ -964,7 +969,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * channel. We still have to poll the channel enable bit due * to AHB/HSB limitations. */ - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); /* disabling channel: must also remove suspend state */ dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask); @@ -985,7 +990,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, /* if channel dedicated to cyclic operations, free it */ clear_bit(ATC_IS_CYCLIC, &atchan->status); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else { return -ENXIO; } @@ -1011,9 +1016,10 @@ atc_tx_status(struct dma_chan *chan, struct at_dma_chan *atchan = to_at_dma_chan(chan); dma_cookie_t last_used; dma_cookie_t last_complete; + unsigned long flags; enum dma_status ret; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); last_complete = atchan->completed_cookie; last_used = chan->cookie; @@ -1028,7 +1034,7 @@ atc_tx_status(struct dma_chan *chan, ret = dma_async_is_complete(cookie, last_complete, last_used); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); if (ret != DMA_SUCCESS) dma_set_tx_state(txstate, last_complete, last_used, @@ -1036,7 +1042,7 @@ atc_tx_status(struct dma_chan *chan, else dma_set_tx_state(txstate, last_complete, last_used, 0); - if (test_bit(ATC_IS_PAUSED, &atchan->status)) + if (atc_chan_is_paused(atchan)) ret = DMA_PAUSED; dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n", @@ -1053,18 +1059,19 @@ atc_tx_status(struct dma_chan *chan, static void atc_issue_pending(struct dma_chan *chan) { struct at_dma_chan *atchan = to_at_dma_chan(chan); + unsigned long flags; dev_vdbg(chan2dev(chan), "issue_pending\n"); /* Not needed for cyclic transfers */ - if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + if (atc_chan_is_cyclic(atchan)) return; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); if (!atc_chan_is_enabled(atchan)) { atc_advance_work(atchan); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } /** @@ -1080,6 +1087,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) struct at_dma *atdma = to_at_dma(chan->device); struct at_desc *desc; struct at_dma_slave *atslave; + unsigned long flags; int i; u32 cfg; LIST_HEAD(tmp_list); @@ -1123,11 +1131,11 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) list_add_tail(&desc->desc_node, &tmp_list); } - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; list_splice(&tmp_list, &atchan->free_list); atchan->completed_cookie = chan->cookie = 1; - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); /* channel parameters */ channel_writel(atchan, CFG, cfg); @@ -1223,7 +1231,7 @@ static int __init at_dma_probe(struct platform_device *pdev) atdma->dma_common.cap_mask = pdata->cap_mask; atdma->all_chan_mask = (1 << pdata->nr_channels) - 1; - size = io->end - io->start + 1; + size = resource_size(io); if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { err = -EBUSY; goto err_kfree; @@ -1267,12 +1275,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++, atdma->dma_common.chancnt++) { + for (i = 0; i < pdata->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; - atchan->chan_common.chan_id = i; list_add_tail(&atchan->chan_common.device_node, &atdma->dma_common.channels); @@ -1300,22 +1307,20 @@ static int __init at_dma_probe(struct platform_device *pdev) if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask)) atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy; - if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) + if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) { atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; - - if (dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask)) + /* controller can do slave DMA: can trigger cyclic transfers */ + dma_cap_set(DMA_CYCLIC, atdma->dma_common.cap_mask); atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic; - - if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) || - dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask)) atdma->dma_common.device_control = atc_control; + } dma_writel(atdma, EN, AT_DMA_ENABLE); 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 " : "", - atdma->dma_common.chancnt); + pdata->nr_channels); dma_async_device_register(&atdma->dma_common); @@ -1369,7 +1374,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) atdma->regs = NULL; io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(io->start, io->end - io->start + 1); + release_mem_region(io->start, resource_size(io)); kfree(atdma); @@ -1384,27 +1389,112 @@ static void at_dma_shutdown(struct platform_device *pdev) clk_disable(atdma->clk); } +static int at_dma_prepare(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; + + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + /* wait for transaction completion (except in cyclic case) */ + if (atc_chan_is_enabled(atchan) && !atc_chan_is_cyclic(atchan)) + return -EAGAIN; + } + return 0; +} + +static void atc_suspend_cyclic(struct at_dma_chan *atchan) +{ + struct dma_chan *chan = &atchan->chan_common; + + /* Channel should be paused by user + * do it anyway even if it is not done already */ + if (!atc_chan_is_paused(atchan)) { + dev_warn(chan2dev(chan), + "cyclic channel not paused, should be done by channel user\n"); + atc_control(chan, DMA_PAUSE, 0); + } + + /* now preserve additional data for cyclic operations */ + /* next descriptor address in the cyclic list */ + atchan->save_dscr = channel_readl(atchan, DSCR); + + vdbg_dump_regs(atchan); +} + static int at_dma_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; - at_dma_off(platform_get_drvdata(pdev)); + /* preserve data */ + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + if (atc_chan_is_cyclic(atchan)) + atc_suspend_cyclic(atchan); + atchan->save_cfg = channel_readl(atchan, CFG); + } + atdma->save_imr = dma_readl(atdma, EBCIMR); + + /* disable DMA controller */ + at_dma_off(atdma); clk_disable(atdma->clk); return 0; } +static void atc_resume_cyclic(struct at_dma_chan *atchan) +{ + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + + /* restore channel status for cyclic descriptors list: + * next descriptor in the cyclic list at the time of suspend */ + channel_writel(atchan, SADDR, 0); + channel_writel(atchan, DADDR, 0); + channel_writel(atchan, CTRLA, 0); + channel_writel(atchan, CTRLB, 0); + channel_writel(atchan, DSCR, atchan->save_dscr); + dma_writel(atdma, CHER, atchan->mask); + + /* channel pause status should be removed by channel user + * We cannot take the initiative to do it here */ + + vdbg_dump_regs(atchan); +} + static int at_dma_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; + /* bring back DMA controller */ clk_enable(atdma->clk); dma_writel(atdma, EN, AT_DMA_ENABLE); + + /* clear any pending interrupt */ + while (dma_readl(atdma, EBCISR)) + cpu_relax(); + + /* restore saved data */ + dma_writel(atdma, EBCIER, atdma->save_imr); + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + channel_writel(atchan, CFG, atchan->save_cfg); + if (atc_chan_is_cyclic(atchan)) + atc_resume_cyclic(atchan); + } return 0; } static const struct dev_pm_ops at_dma_dev_pm_ops = { + .prepare = at_dma_prepare, .suspend_noirq = at_dma_suspend_noirq, .resume_noirq = at_dma_resume_noirq, }; diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 19ed470..5aa82b4 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -204,6 +204,9 @@ enum atc_status { * @status: transmit status information from irq/prep* functions * to tasklet (use atomic operations) * @tasklet: bottom half to finish transaction work + * @save_cfg: configuration register that is saved on suspend/resume cycle + * @save_dscr: for cyclic operations, preserve next descriptor address in + * the cyclic list on suspend/resume cycle * @lock: serializes enqueue/dequeue operations to descriptors lists * @completed_cookie: identifier for the most recently completed operation * @active_list: list of descriptors dmaengine is being running on @@ -218,6 +221,8 @@ struct at_dma_chan { u8 mask; unsigned long status; struct tasklet_struct tasklet; + u32 save_cfg; + u32 save_dscr; spinlock_t lock; @@ -248,6 +253,7 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) * @chan_common: common dmaengine dma_device object members * @ch_regs: memory mapped register base * @clk: dma controller clock + * @save_imr: interrupt mask register that is saved on suspend/resume cycle * @all_chan_mask: all channels availlable in a mask * @dma_desc_pool: base of DMA descriptor region (DMA address) * @chan: channels table to store at_dma_chan structures @@ -256,6 +262,7 @@ struct at_dma { struct dma_device dma_common; void __iomem *regs; struct clk *clk; + u32 save_imr; u8 all_chan_mask; @@ -354,6 +361,23 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan) return !!(dma_readl(atdma, CHSR) & atchan->mask); } +/** + * atc_chan_is_paused - test channel pause/resume status + * @atchan: channel we want to test status + */ +static inline int atc_chan_is_paused(struct at_dma_chan *atchan) +{ + return test_bit(ATC_IS_PAUSED, &atchan->status); +} + +/** + * atc_chan_is_cyclic - test if given channel has cyclic property set + * @atchan: channel we want to test status + */ +static inline int atc_chan_is_cyclic(struct at_dma_chan *atchan) +{ + return test_bit(ATC_IS_CYCLIC, &atchan->status); +} /** * set_desc_eol - set end-of-link to descriptor so it will end transfer diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index af8c0b5..4234f41 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -11,6 +11,7 @@ #include #include /* printk() */ #include /* everything... */ +#include #include /* kmalloc() */ #include #include @@ -40,6 +41,8 @@ struct coh901318_desc { struct coh901318_lli *lli; enum dma_data_direction dir; unsigned long flags; + u32 head_config; + u32 head_ctrl; }; struct coh901318_base { @@ -660,6 +663,9 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc) coh901318_desc_submit(cohc, cohd); + /* Program the transaction head */ + coh901318_set_conf(cohc, cohd->head_config); + coh901318_set_ctrl(cohc, cohd->head_ctrl); coh901318_prep_linked_list(cohc, cohd->lli); /* start dma job on this channel */ @@ -1090,8 +1096,6 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } else goto err_direction; - coh901318_set_conf(cohc, config); - /* The dma only supports transmitting packages up to * MAX_DMA_PACKET_SIZE. Calculate to total number of * dma elemts required to send the entire sg list @@ -1128,16 +1132,18 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (ret) goto err_lli_fill; - /* - * Set the default ctrl for the channel to the one from the lli, - * things may have changed due to odd buffer alignment etc. - */ - coh901318_set_ctrl(cohc, lli->control); COH_DBG(coh901318_list_print(cohc, lli)); /* Pick a descriptor to handle this transfer */ cohd = coh901318_desc_get(cohc); + cohd->head_config = config; + /* + * Set the default head ctrl for the channel to the one from the + * lli, things may have changed due to odd buffer alignment + * etc. + */ + cohd->head_ctrl = lli->control; cohd->dir = direction; cohd->flags = flags; cohd->desc.tx_submit = coh901318_tx_submit; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 8bcb15f..5991114 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -45,6 +45,7 @@ * See Documentation/dmaengine.txt for more details */ +#include #include #include #include @@ -61,9 +62,9 @@ #include static DEFINE_MUTEX(dma_list_mutex); +static DEFINE_IDR(dma_idr); static LIST_HEAD(dma_device_list); static long dmaengine_ref_count; -static struct idr dma_idr; /* --- sysfs implementation --- */ @@ -509,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v dma_chan_name(chan)); list_del_rcu(&device->global_node); } else if (err) - pr_err("dmaengine: failed to get %s: (%d)\n", - dma_chan_name(chan), err); + pr_debug("dmaengine: failed to get %s: (%d)\n", + dma_chan_name(chan), err); else break; if (--device->privatecnt == 0) @@ -563,8 +564,8 @@ void dmaengine_get(void) list_del_rcu(&device->global_node); break; } else if (err) - pr_err("dmaengine: failed to get %s: (%d)\n", - dma_chan_name(chan), err); + pr_debug("%s: failed to get %s: (%d)\n", + __func__, dma_chan_name(chan), err); } } @@ -1049,8 +1050,6 @@ EXPORT_SYMBOL_GPL(dma_run_dependencies); static int __init dma_bus_init(void) { - idr_init(&dma_idr); - mutex_init(&dma_list_mutex); return class_register(&dma_devclass); } arch_initcall(dma_bus_init); diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index b4f5c32..eb1d864 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -8,7 +8,9 @@ * published by the Free Software Foundation. */ #include +#include #include +#include #include #include #include @@ -250,6 +252,7 @@ static int dmatest_func(void *data) int i; thread_name = current->comm; + set_freezable_with_signal(); ret = -ENOMEM; @@ -304,7 +307,8 @@ static int dmatest_func(void *data) dma_addr_t dma_srcs[src_cnt]; dma_addr_t dma_dsts[dst_cnt]; struct completion cmp; - unsigned long tmo = msecs_to_jiffies(timeout); + unsigned long start, tmo, end = 0 /* compiler... */; + bool reload = true; u8 align = 0; total_tests++; @@ -403,7 +407,17 @@ static int dmatest_func(void *data) } dma_async_issue_pending(chan); - tmo = wait_for_completion_timeout(&cmp, tmo); + do { + start = jiffies; + if (reload) + end = start + msecs_to_jiffies(timeout); + else if (end <= start) + end = start + 1; + tmo = wait_for_completion_interruptible_timeout(&cmp, + end - start); + reload = try_to_freeze(); + } while (tmo == -ERESTARTSYS); + status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); if (tmo == 0) { @@ -476,6 +490,8 @@ err_srcs: pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", thread_name, total_tests, failed_tests, ret); + /* terminate all transfers on specified channels */ + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); @@ -498,6 +514,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) list_del(&thread->node); kfree(thread); } + + /* terminate all transfers on specified channels */ + dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0); + kfree(dtc); } diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 4d180ca..9bfd6d3 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1407,12 +1407,11 @@ static int __init dw_probe(struct platform_device *pdev) dw->all_chan_mask = (1 << pdata->nr_channels) - 1; INIT_LIST_HEAD(&dw->dma.channels); - for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) { + for (i = 0; i < pdata->nr_channels; i++) { struct dw_dma_chan *dwc = &dw->chan[i]; dwc->chan.device = &dw->dma; dwc->chan.cookie = dwc->completed = 1; - dwc->chan.chan_id = i; if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) list_add_tail(&dwc->chan.device_node, &dw->dma.channels); @@ -1468,7 +1467,7 @@ static int __init 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), dw->dma.chancnt); + dev_name(&pdev->dev), pdata->nr_channels); dma_async_device_register(&dw->dma); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index e18eaab..4be55f9 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -14,6 +14,7 @@ * http://www.gnu.org/copyleft/gpl.html */ #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -135,7 +137,8 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, if (ret) return ret; - imx_dma_config_burstlen(imxdmac->imxdma_channel, imxdmac->watermark_level); + imx_dma_config_burstlen(imxdmac->imxdma_channel, + imxdmac->watermark_level * imxdmac->word_size); return 0; default: diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index b6d1455..f993955 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,9 @@ #include #include #include +#include +#include +#include #include #include @@ -65,8 +69,8 @@ #define SDMA_ONCE_RTB 0x060 #define SDMA_XTRIG_CONF1 0x070 #define SDMA_XTRIG_CONF2 0x074 -#define SDMA_CHNENBL0_V2 0x200 -#define SDMA_CHNENBL0_V1 0x080 +#define SDMA_CHNENBL0_IMX35 0x200 +#define SDMA_CHNENBL0_IMX31 0x080 #define SDMA_CHNPRI_0 0x100 /* @@ -299,21 +303,47 @@ struct sdma_firmware_header { u32 ram_code_size; }; +enum sdma_devtype { + IMX31_SDMA, /* runs on i.mx31 */ + IMX35_SDMA, /* runs on i.mx35 and later */ +}; + struct sdma_engine { struct device *dev; struct device_dma_parameters dma_parms; struct sdma_channel channel[MAX_DMA_CHANNELS]; struct sdma_channel_control *channel_control; void __iomem *regs; - unsigned int version; + enum sdma_devtype devtype; unsigned int num_events; struct sdma_context_data *context; dma_addr_t context_phys; struct dma_device dma_device; struct clk *clk; + struct mutex channel_0_lock; struct sdma_script_start_addrs *script_addrs; }; +static struct platform_device_id sdma_devtypes[] = { + { + .name = "imx31-sdma", + .driver_data = IMX31_SDMA, + }, { + .name = "imx35-sdma", + .driver_data = IMX35_SDMA, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, sdma_devtypes); + +static const struct of_device_id sdma_dt_ids[] = { + { .compatible = "fsl,imx31-sdma", .data = &sdma_devtypes[IMX31_SDMA], }, + { .compatible = "fsl,imx35-sdma", .data = &sdma_devtypes[IMX35_SDMA], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sdma_dt_ids); + #define SDMA_H_CONFIG_DSPDMA (1 << 12) /* indicates if the DSPDMA is used */ #define SDMA_H_CONFIG_RTD_PINS (1 << 11) /* indicates if Real-Time Debug pins are enabled */ #define SDMA_H_CONFIG_ACR (1 << 4) /* indicates if AHB freq /core freq = 2 or 1 */ @@ -321,8 +351,8 @@ struct sdma_engine { static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event) { - u32 chnenbl0 = (sdma->version == 2 ? SDMA_CHNENBL0_V2 : SDMA_CHNENBL0_V1); - + u32 chnenbl0 = (sdma->devtype == IMX31_SDMA ? SDMA_CHNENBL0_IMX31 : + SDMA_CHNENBL0_IMX35); return chnenbl0 + event * 4; } @@ -388,11 +418,15 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, dma_addr_t buf_phys; int ret; + mutex_lock(&sdma->channel_0_lock); + buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); - if (!buf_virt) - return -ENOMEM; + if (!buf_virt) { + ret = -ENOMEM; + goto err_out; + } bd0->mode.command = C0_SETPM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; @@ -406,6 +440,9 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, dma_free_coherent(NULL, size, buf_virt, buf_phys); +err_out: + mutex_unlock(&sdma->channel_0_lock); + return ret; } @@ -629,6 +666,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0); dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1); + mutex_lock(&sdma->channel_0_lock); + memset(context, 0, sizeof(*context)); context->channel_state.pc = load_address; @@ -649,6 +688,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) ret = sdma_run_channel(&sdma->channel[0]); + mutex_unlock(&sdma->channel_0_lock); + return ret; } @@ -1104,26 +1145,17 @@ static void sdma_add_scripts(struct sdma_engine *sdma, saddr_arr[i] = addr_arr[i]; } -static int __init sdma_get_firmware(struct sdma_engine *sdma, - const char *cpu_name, int to_version) +static void sdma_load_firmware(const struct firmware *fw, void *context) { - const struct firmware *fw; - char *fwname; + struct sdma_engine *sdma = context; const struct sdma_firmware_header *header; - int ret; const struct sdma_script_start_addrs *addr; unsigned short *ram_code; - fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", cpu_name, to_version); - if (!fwname) - return -ENOMEM; - - ret = request_firmware(&fw, fwname, sdma->dev); - if (ret) { - kfree(fwname); - return ret; + if (!fw) { + dev_err(sdma->dev, "firmware not found\n"); + return; } - kfree(fwname); if (fw->size < sizeof(*header)) goto err_firmware; @@ -1153,6 +1185,16 @@ static int __init sdma_get_firmware(struct sdma_engine *sdma, err_firmware: release_firmware(fw); +} + +static int __init sdma_get_firmware(struct sdma_engine *sdma, + const char *fw_name) +{ + int ret; + + ret = request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, fw_name, sdma->dev, + GFP_KERNEL, sdma, sdma_load_firmware); return ret; } @@ -1162,15 +1204,16 @@ static int __init sdma_init(struct sdma_engine *sdma) int i, ret; dma_addr_t ccb_phys; - switch (sdma->version) { - case 1: + switch (sdma->devtype) { + case IMX31_SDMA: sdma->num_events = 32; break; - case 2: + case IMX35_SDMA: sdma->num_events = 48; break; default: - dev_err(sdma->dev, "Unknown version %d. aborting\n", sdma->version); + dev_err(sdma->dev, "Unknown sdma type %d. aborting\n", + sdma->devtype); return -ENODEV; } @@ -1239,22 +1282,29 @@ err_dma_alloc: static int __init sdma_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(sdma_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; + const char *fw_name; int ret; int irq; struct resource *iores; struct sdma_platform_data *pdata = pdev->dev.platform_data; int i; struct sdma_engine *sdma; + s32 *saddr_arr; sdma = kzalloc(sizeof(*sdma), GFP_KERNEL); if (!sdma) return -ENOMEM; + mutex_init(&sdma->channel_0_lock); + sdma->dev = &pdev->dev; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!iores || irq < 0 || !pdata) { + if (!iores || irq < 0) { ret = -EINVAL; goto err_irq; } @@ -1281,10 +1331,19 @@ static int __init sdma_probe(struct platform_device *pdev) goto err_request_irq; sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL); - if (!sdma->script_addrs) + if (!sdma->script_addrs) { + ret = -ENOMEM; goto err_alloc; + } + + /* initially no scripts available */ + saddr_arr = (s32 *)sdma->script_addrs; + for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++) + saddr_arr[i] = -EINVAL; - sdma->version = pdata->sdma_version; + if (of_id) + pdev->id_entry = of_id->data; + sdma->devtype = pdev->id_entry->driver_data; dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask); dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask); @@ -1314,10 +1373,30 @@ static int __init sdma_probe(struct platform_device *pdev) if (ret) goto err_init; - if (pdata->script_addrs) + if (pdata && pdata->script_addrs) sdma_add_scripts(sdma, pdata->script_addrs); - sdma_get_firmware(sdma, pdata->cpu_name, pdata->to_version); + if (pdata) { + sdma_get_firmware(sdma, pdata->fw_name); + } else { + /* + * Because that device tree does not encode ROM script address, + * the RAM script in firmware is mandatory for device tree + * probe, otherwise it fails. + */ + ret = of_property_read_string(np, "fsl,sdma-ram-script-name", + &fw_name); + if (ret) { + dev_err(&pdev->dev, "failed to get firmware name\n"); + goto err_init; + } + + ret = sdma_get_firmware(sdma, fw_name); + if (ret) { + dev_err(&pdev->dev, "failed to get firmware\n"); + goto err_init; + } + } sdma->dma_device.dev = &pdev->dev; @@ -1365,7 +1444,9 @@ static int __exit sdma_remove(struct platform_device *pdev) static struct platform_driver sdma_driver = { .driver = { .name = "imx-sdma", + .of_match_table = sdma_dt_ids, }, + .id_table = sdma_devtypes, .remove = __exit_p(sdma_remove), }; diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index f653517..19a0c64 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -27,6 +27,7 @@ #include #include #include +#include #define MAX_CHAN 4 /*max ch across controllers*/ #include "intel_mid_dma_regs.h" @@ -115,16 +116,15 @@ DMAC1 interrupt Functions*/ /** * dmac1_mask_periphral_intr - mask the periphral interrupt - * @midc: dma channel for which masking is required + * @mid: dma device for which masking is required * * Masks the DMA periphral interrupt * this is valid for DMAC1 family controllers only * This controller should have periphral mask registers already mapped */ -static void dmac1_mask_periphral_intr(struct intel_mid_dma_chan *midc) +static void dmac1_mask_periphral_intr(struct middma_device *mid) { u32 pimr; - struct middma_device *mid = to_middma_device(midc->chan.device); if (mid->pimr_mask) { pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK); @@ -184,7 +184,6 @@ static void enable_dma_interrupt(struct intel_mid_dma_chan *midc) static void disable_dma_interrupt(struct intel_mid_dma_chan *midc) { /*Check LPE PISR, make sure fwd is disabled*/ - dmac1_mask_periphral_intr(midc); iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_BLOCK); iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR); iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR); @@ -1114,7 +1113,6 @@ static int mid_setup_dma(struct pci_dev *pdev) midch->chan.device = &dma->common; midch->chan.cookie = 1; - midch->chan.chan_id = i; midch->ch_id = dma->chan_base + i; pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id); @@ -1150,7 +1148,6 @@ static int mid_setup_dma(struct pci_dev *pdev) dma_cap_set(DMA_SLAVE, dma->common.cap_mask); dma_cap_set(DMA_PRIVATE, dma->common.cap_mask); dma->common.dev = &pdev->dev; - dma->common.chancnt = dma->max_chan; dma->common.device_alloc_chan_resources = intel_mid_dma_alloc_chan_resources; @@ -1350,8 +1347,8 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state) if (device->ch[i].in_use) return -EAGAIN; } + dmac1_mask_periphral_intr(device); device->state = SUSPENDED; - pci_set_drvdata(pci, device); pci_save_state(pci); pci_disable_device(pci); pci_set_power_state(pci, PCI_D3hot); @@ -1380,7 +1377,6 @@ int dma_resume(struct pci_dev *pci) } device->state = RUNNING; iowrite32(REG_BIT0, device->dma_base + DMA_CFG); - pci_set_drvdata(pci, device); return 0; } diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index a4d6cb0..3f89386 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -75,7 +75,8 @@ static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) { chan = ioat_chan_by_index(instance, bit); - tasklet_schedule(&chan->cleanup_task); + if (test_bit(IOAT_RUN, &chan->state)) + tasklet_schedule(&chan->cleanup_task); } writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); @@ -91,7 +92,8 @@ static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) { struct ioat_chan_common *chan = data; - tasklet_schedule(&chan->cleanup_task); + if (test_bit(IOAT_RUN, &chan->state)) + tasklet_schedule(&chan->cleanup_task); return IRQ_HANDLED; } @@ -113,7 +115,6 @@ void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *c chan->timer.function = device->timer_fn; chan->timer.data = data; tasklet_init(&chan->cleanup_task, device->cleanup_fn, data); - tasklet_disable(&chan->cleanup_task); } /** @@ -356,13 +357,43 @@ static int ioat1_dma_alloc_chan_resources(struct dma_chan *c) writel(((u64) chan->completion_dma) >> 32, chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); - tasklet_enable(&chan->cleanup_task); + set_bit(IOAT_RUN, &chan->state); ioat1_dma_start_null_desc(ioat); /* give chain to dma device */ dev_dbg(to_dev(chan), "%s: allocated %d descriptors\n", __func__, ioat->desccount); return ioat->desccount; } +void ioat_stop(struct ioat_chan_common *chan) +{ + struct ioatdma_device *device = chan->device; + struct pci_dev *pdev = device->pdev; + int chan_id = chan_num(chan); + + /* 1/ stop irq from firing tasklets + * 2/ stop the tasklet from re-arming irqs + */ + clear_bit(IOAT_RUN, &chan->state); + + /* flush inflight interrupts */ +#ifdef CONFIG_PCI_MSI + if (pdev->msix_enabled) { + struct msix_entry *msix = &device->msix_entries[chan_id]; + synchronize_irq(msix->vector); + } else +#endif + synchronize_irq(pdev->irq); + + /* flush inflight timers */ + del_timer_sync(&chan->timer); + + /* flush inflight tasklet runs */ + tasklet_kill(&chan->cleanup_task); + + /* final cleanup now that everything is quiesced and can't re-arm */ + device->cleanup_fn((unsigned long) &chan->common); +} + /** * ioat1_dma_free_chan_resources - release all the descriptors * @chan: the channel to be cleaned @@ -381,9 +412,7 @@ static void ioat1_dma_free_chan_resources(struct dma_chan *c) if (ioat->desccount == 0) return; - tasklet_disable(&chan->cleanup_task); - del_timer_sync(&chan->timer); - ioat1_cleanup(ioat); + ioat_stop(chan); /* Delay 100ms after reset to allow internal DMA logic to quiesce * before removing DMA descriptor resources. @@ -528,8 +557,11 @@ ioat1_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dest, static void ioat1_cleanup_event(unsigned long data) { struct ioat_dma_chan *ioat = to_ioat_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; ioat1_cleanup(ioat); + if (!test_bit(IOAT_RUN, &chan->state)) + return; writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); } @@ -548,9 +580,9 @@ void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, PCI_DMA_TODEVICE, flags, 0); } -unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) +dma_addr_t ioat_get_current_completion(struct ioat_chan_common *chan) { - unsigned long phys_complete; + dma_addr_t phys_complete; u64 completion; completion = *chan->completion; @@ -571,7 +603,7 @@ unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) } bool ioat_cleanup_preamble(struct ioat_chan_common *chan, - unsigned long *phys_complete) + dma_addr_t *phys_complete) { *phys_complete = ioat_get_current_completion(chan); if (*phys_complete == chan->last_completion) @@ -582,14 +614,14 @@ bool ioat_cleanup_preamble(struct ioat_chan_common *chan, return true; } -static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct list_head *_desc, *n; struct dma_async_tx_descriptor *tx; - dev_dbg(to_dev(chan), "%s: phys_complete: %lx\n", - __func__, phys_complete); + dev_dbg(to_dev(chan), "%s: phys_complete: %llx\n", + __func__, (unsigned long long) phys_complete); list_for_each_safe(_desc, n, &ioat->used_desc) { struct ioat_desc_sw *desc; @@ -655,7 +687,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) static void ioat1_cleanup(struct ioat_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; prefetch(chan->completion); @@ -701,7 +733,7 @@ static void ioat1_timer_event(unsigned long data) mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); spin_unlock_bh(&ioat->desc_lock); } else if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&ioat->desc_lock); /* if we haven't made progress and we have already diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 5216c8a..466e0f34c 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -88,7 +88,7 @@ struct ioatdma_device { struct ioat_chan_common { struct dma_chan common; void __iomem *reg_base; - unsigned long last_completion; + dma_addr_t last_completion; spinlock_t cleanup_lock; dma_cookie_t completed_cookie; unsigned long state; @@ -333,7 +333,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device); void __devexit ioat_dma_remove(struct ioatdma_device *device); struct dca_provider * __devinit ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); -unsigned long ioat_get_current_completion(struct ioat_chan_common *chan); +dma_addr_t ioat_get_current_completion(struct ioat_chan_common *chan); void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *chan, int idx); enum dma_status ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, @@ -341,9 +341,10 @@ enum dma_status ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, size_t len, struct ioat_dma_descriptor *hw); bool ioat_cleanup_preamble(struct ioat_chan_common *chan, - unsigned long *phys_complete); + dma_addr_t *phys_complete); void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type); void ioat_kobject_del(struct ioatdma_device *device); +void ioat_stop(struct ioat_chan_common *chan); extern const struct sysfs_ops ioat_sysfs_ops; extern struct ioat_sysfs_entry ioat_version_attr; extern struct ioat_sysfs_entry ioat_cap_attr; diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 5d65f83..e60933e 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -126,7 +126,7 @@ static void ioat2_start_null_desc(struct ioat2_dma_chan *ioat) spin_unlock_bh(&ioat->prep_lock); } -static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat2_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct dma_async_tx_descriptor *tx; @@ -178,7 +178,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) static void ioat2_cleanup(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&chan->cleanup_lock); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -189,8 +189,11 @@ static void ioat2_cleanup(struct ioat2_dma_chan *ioat) void ioat2_cleanup_event(unsigned long data) { struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; ioat2_cleanup(ioat); + if (!test_bit(IOAT_RUN, &chan->state)) + return; writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); } @@ -259,7 +262,7 @@ int ioat2_reset_sync(struct ioat_chan_common *chan, unsigned long tmo) static void ioat2_restart_channel(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; ioat2_quiesce(chan, 0); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -274,7 +277,7 @@ void ioat2_timer_event(unsigned long data) struct ioat_chan_common *chan = &ioat->base; if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; u64 status; status = ioat_chansts(chan); @@ -542,10 +545,10 @@ int ioat2_alloc_chan_resources(struct dma_chan *c) ioat->issued = 0; ioat->tail = 0; ioat->alloc_order = order; + set_bit(IOAT_RUN, &chan->state); spin_unlock_bh(&ioat->prep_lock); spin_unlock_bh(&chan->cleanup_lock); - tasklet_enable(&chan->cleanup_task); ioat2_start_null_desc(ioat); /* check that we got off the ground */ @@ -555,7 +558,6 @@ int ioat2_alloc_chan_resources(struct dma_chan *c) } while (i++ < 20 && !is_ioat_active(status) && !is_ioat_idle(status)); if (is_ioat_active(status) || is_ioat_idle(status)) { - set_bit(IOAT_RUN, &chan->state); return 1 << ioat->alloc_order; } else { u32 chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); @@ -798,11 +800,8 @@ void ioat2_free_chan_resources(struct dma_chan *c) if (!ioat->ring) return; - tasklet_disable(&chan->cleanup_task); - del_timer_sync(&chan->timer); - device->cleanup_fn((unsigned long) c); + ioat_stop(chan); device->reset_hw(chan); - clear_bit(IOAT_RUN, &chan->state); spin_lock_bh(&chan->cleanup_lock); spin_lock_bh(&ioat->prep_lock); diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 6e33926..8680031 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -73,10 +73,10 @@ /* provide a lookup table for setting the source address in the base or * extended descriptor of an xor or pq descriptor */ -static const u8 xor_idx_to_desc __read_mostly = 0xd0; -static const u8 xor_idx_to_field[] __read_mostly = { 1, 4, 5, 6, 7, 0, 1, 2 }; -static const u8 pq_idx_to_desc __read_mostly = 0xf8; -static const u8 pq_idx_to_field[] __read_mostly = { 1, 4, 5, 0, 1, 2, 4, 5 }; +static const u8 xor_idx_to_desc = 0xe0; +static const u8 xor_idx_to_field[] = { 1, 4, 5, 6, 7, 0, 1, 2 }; +static const u8 pq_idx_to_desc = 0xf8; +static const u8 pq_idx_to_field[] = { 1, 4, 5, 0, 1, 2, 4, 5 }; static dma_addr_t xor_get_src(struct ioat_raw_descriptor *descs[2], int idx) { @@ -256,7 +256,7 @@ static bool desc_has_ext(struct ioat_ring_ent *desc) * The difference from the dma_v2.c __cleanup() is that this routine * handles extended descriptors and dma-unmapping raid operations. */ -static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat2_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct ioat_ring_ent *desc; @@ -314,7 +314,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) static void ioat3_cleanup(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&chan->cleanup_lock); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -325,15 +325,18 @@ static void ioat3_cleanup(struct ioat2_dma_chan *ioat) static void ioat3_cleanup_event(unsigned long data) { struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; ioat3_cleanup(ioat); + if (!test_bit(IOAT_RUN, &chan->state)) + return; writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); } static void ioat3_restart_channel(struct ioat2_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; ioat2_quiesce(chan, 0); if (ioat_cleanup_preamble(chan, &phys_complete)) @@ -348,7 +351,7 @@ static void ioat3_timer_event(unsigned long data) struct ioat_chan_common *chan = &ioat->base; if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; u64 status; status = ioat_chansts(chan); diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index fab37d1..5e3a40f 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -72,6 +72,17 @@ static struct pci_device_id ioat_pci_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF8) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF9) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB0) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB1) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB2) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB3) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB4) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB5) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB6) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB8) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB9) }, + { 0, } }; MODULE_DEVICE_TABLE(pci, ioat_pci_tbl); diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index c1a125e..0e5ef33 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include @@ -1306,6 +1308,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { callback = descnew->txd.callback; callback_param = descnew->txd.callback_param; + list_del_init(&descnew->list); spin_unlock(&ichan->lock); if (callback) callback(callback_param); @@ -1427,39 +1430,58 @@ static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac *idmac = to_idmac(chan->device); + struct ipu *ipu = to_ipu(idmac); + struct list_head *list, *tmp; unsigned long flags; int i; - /* Only supports DMA_TERMINATE_ALL */ - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; + switch (cmd) { + case DMA_PAUSE: + spin_lock_irqsave(&ipu->lock, flags); + ipu_ic_disable_task(ipu, chan->chan_id); - ipu_disable_channel(idmac, ichan, - ichan->status >= IPU_CHANNEL_ENABLED); + /* Return all descriptors into "prepared" state */ + list_for_each_safe(list, tmp, &ichan->queue) + list_del_init(list); - tasklet_disable(&to_ipu(idmac)->tasklet); + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; - /* ichan->queue is modified in ISR, have to spinlock */ - spin_lock_irqsave(&ichan->lock, flags); - list_splice_init(&ichan->queue, &ichan->free_list); + spin_unlock_irqrestore(&ipu->lock, flags); - if (ichan->desc) - for (i = 0; i < ichan->n_tx_desc; i++) { - struct idmac_tx_desc *desc = ichan->desc + i; - if (list_empty(&desc->list)) - /* Descriptor was prepared, but not submitted */ - list_add(&desc->list, &ichan->free_list); + ichan->status = IPU_CHANNEL_INITIALIZED; + break; + case DMA_TERMINATE_ALL: + ipu_disable_channel(idmac, ichan, + ichan->status >= IPU_CHANNEL_ENABLED); - async_tx_clear_ack(&desc->txd); - } + tasklet_disable(&ipu->tasklet); - ichan->sg[0] = NULL; - ichan->sg[1] = NULL; - spin_unlock_irqrestore(&ichan->lock, flags); + /* ichan->queue is modified in ISR, have to spinlock */ + spin_lock_irqsave(&ichan->lock, flags); + list_splice_init(&ichan->queue, &ichan->free_list); - tasklet_enable(&to_ipu(idmac)->tasklet); + if (ichan->desc) + for (i = 0; i < ichan->n_tx_desc; i++) { + struct idmac_tx_desc *desc = ichan->desc + i; + if (list_empty(&desc->list)) + /* Descriptor was prepared, but not submitted */ + list_add(&desc->list, &ichan->free_list); - ichan->status = IPU_CHANNEL_INITIALIZED; + async_tx_clear_ack(&desc->txd); + } + + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; + spin_unlock_irqrestore(&ichan->lock, flags); + + tasklet_enable(&ipu->tasklet); + + ichan->status = IPU_CHANNEL_INITIALIZED; + break; + default: + return -ENOSYS; + } return 0; } @@ -1662,7 +1684,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu) struct idmac_channel *ichan = ipu->channel + i; idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0); - idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0); } dma_async_device_unregister(&idmac->dma); @@ -1705,16 +1726,14 @@ static int __init ipu_probe(struct platform_device *pdev) ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base); /* Remap IPU common registers */ - ipu_data.reg_ipu = ioremap(mem_ipu->start, - mem_ipu->end - mem_ipu->start + 1); + ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu)); if (!ipu_data.reg_ipu) { ret = -ENOMEM; goto err_ioremap_ipu; } /* Remap Image Converter and Image DMA Controller registers */ - ipu_data.reg_ic = ioremap(mem_ic->start, - mem_ic->end - mem_ic->start + 1); + ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic)); if (!ipu_data.reg_ic) { ret = -ENOMEM; goto err_ioremap_ic; diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index ab8a4ef..a71f55e 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -81,7 +81,7 @@ static struct ipu_irq_map irq_map[CONFIG_MX3_IPU_IRQS]; /* Protects allocations from the above array of maps */ static DEFINE_MUTEX(map_lock); /* Protects register accesses and individual mappings */ -static DEFINE_SPINLOCK(bank_lock); +static DEFINE_RAW_SPINLOCK(bank_lock); static struct ipu_irq_map *src2map(unsigned int src) { @@ -101,11 +101,11 @@ static void ipu_irq_unmask(struct irq_data *d) uint32_t reg; unsigned long lock_flags; - spin_lock_irqsave(&bank_lock, lock_flags); + raw_spin_lock_irqsave(&bank_lock, lock_flags); bank = map->bank; if (!bank) { - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq); return; } @@ -114,7 +114,7 @@ static void ipu_irq_unmask(struct irq_data *d) reg |= (1UL << (map->source & 31)); ipu_write_reg(bank->ipu, reg, bank->control); - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); } static void ipu_irq_mask(struct irq_data *d) @@ -124,11 +124,11 @@ static void ipu_irq_mask(struct irq_data *d) uint32_t reg; unsigned long lock_flags; - spin_lock_irqsave(&bank_lock, lock_flags); + raw_spin_lock_irqsave(&bank_lock, lock_flags); bank = map->bank; if (!bank) { - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq); return; } @@ -137,7 +137,7 @@ static void ipu_irq_mask(struct irq_data *d) reg &= ~(1UL << (map->source & 31)); ipu_write_reg(bank->ipu, reg, bank->control); - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); } static void ipu_irq_ack(struct irq_data *d) @@ -146,17 +146,17 @@ static void ipu_irq_ack(struct irq_data *d) struct ipu_irq_bank *bank; unsigned long lock_flags; - spin_lock_irqsave(&bank_lock, lock_flags); + raw_spin_lock_irqsave(&bank_lock, lock_flags); bank = map->bank; if (!bank) { - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq); return; } ipu_write_reg(bank->ipu, 1UL << (map->source & 31), bank->status); - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); } /** @@ -172,11 +172,11 @@ bool ipu_irq_status(unsigned int irq) unsigned long lock_flags; bool ret; - spin_lock_irqsave(&bank_lock, lock_flags); + raw_spin_lock_irqsave(&bank_lock, lock_flags); bank = map->bank; ret = bank && ipu_read_reg(bank->ipu, bank->status) & (1UL << (map->source & 31)); - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); return ret; } @@ -213,10 +213,10 @@ int ipu_irq_map(unsigned int source) if (irq_map[i].source < 0) { unsigned long lock_flags; - spin_lock_irqsave(&bank_lock, lock_flags); + raw_spin_lock_irqsave(&bank_lock, lock_flags); irq_map[i].source = source; irq_map[i].bank = irq_bank + source / 32; - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); ret = irq_map[i].irq; pr_debug("IPU: mapped source %u to IRQ %u\n", @@ -252,10 +252,10 @@ int ipu_irq_unmap(unsigned int source) pr_debug("IPU: unmapped source %u from IRQ %u\n", source, irq_map[i].irq); - spin_lock_irqsave(&bank_lock, lock_flags); + raw_spin_lock_irqsave(&bank_lock, lock_flags); irq_map[i].source = -EINVAL; irq_map[i].bank = NULL; - spin_unlock_irqrestore(&bank_lock, lock_flags); + raw_spin_unlock_irqrestore(&bank_lock, lock_flags); ret = 0; break; @@ -276,7 +276,7 @@ static void ipu_irq_err(unsigned int irq, struct irq_desc *desc) for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) { struct ipu_irq_bank *bank = irq_bank + i; - spin_lock(&bank_lock); + raw_spin_lock(&bank_lock); status = ipu_read_reg(ipu, bank->status); /* * Don't think we have to clear all interrupts here, they will @@ -284,18 +284,18 @@ static void ipu_irq_err(unsigned int irq, struct irq_desc *desc) * might want to clear unhandled interrupts after the loop... */ status &= ipu_read_reg(ipu, bank->control); - spin_unlock(&bank_lock); + raw_spin_unlock(&bank_lock); while ((line = ffs(status))) { struct ipu_irq_map *map; line--; status &= ~(1UL << line); - spin_lock(&bank_lock); + raw_spin_lock(&bank_lock); map = src2map(32 * i + line); if (map) irq = map->irq; - spin_unlock(&bank_lock); + raw_spin_unlock(&bank_lock); if (!map) { pr_err("IPU: Interrupt on unmapped source %u bank %d\n", @@ -317,22 +317,22 @@ static void ipu_irq_fn(unsigned int irq, struct irq_desc *desc) for (i = 0; i < IPU_IRQ_NR_FN_BANKS; i++) { struct ipu_irq_bank *bank = irq_bank + i; - spin_lock(&bank_lock); + raw_spin_lock(&bank_lock); status = ipu_read_reg(ipu, bank->status); /* Not clearing all interrupts, see above */ status &= ipu_read_reg(ipu, bank->control); - spin_unlock(&bank_lock); + raw_spin_unlock(&bank_lock); while ((line = ffs(status))) { struct ipu_irq_map *map; line--; status &= ~(1UL << line); - spin_lock(&bank_lock); + raw_spin_lock(&bank_lock); map = src2map(32 * i + line); if (map) irq = map->irq; - spin_unlock(&bank_lock); + raw_spin_unlock(&bank_lock); if (!map) { pr_err("IPU: Interrupt on unmapped source %u bank %d\n", diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index b9bae94..8ba4edc 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -741,7 +741,6 @@ static int __devinit mpc_dma_probe(struct platform_device *op) mchan = &mdma->channels[i]; mchan->chan.device = dma; - mchan->chan.chan_id = i; mchan->chan.cookie = 1; mchan->completed_cookie = mchan->chan.cookie; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 954e334..a258101 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -218,12 +218,10 @@ static void mv_set_mode(struct mv_xor_chan *chan, static void mv_chan_activate(struct mv_xor_chan *chan) { - u32 activation; - dev_dbg(chan->device->common.dev, " activate chan.\n"); - activation = __raw_readl(XOR_ACTIVATION(chan)); - activation |= 0x1; - __raw_writel(activation, XOR_ACTIVATION(chan)); + + /* writel ensures all descriptors are flushed before activation */ + writel(BIT(0), XOR_ACTIVATION(chan)); } static char mv_chan_is_busy(struct mv_xor_chan *chan) @@ -388,7 +386,8 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan) dma_cookie_t cookie = 0; int busy = mv_chan_is_busy(mv_chan); u32 current_desc = mv_chan_get_current_desc(mv_chan); - int seen_current = 0; + int current_cleaned = 0; + struct mv_xor_desc *hw_desc; dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__); dev_dbg(mv_chan->device->common.dev, "current_desc %x\n", current_desc); @@ -400,38 +399,57 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan) list_for_each_entry_safe(iter, _iter, &mv_chan->chain, chain_node) { - prefetch(_iter); - prefetch(&_iter->async_tx); - /* do not advance past the current descriptor loaded into the - * hardware channel, subsequent descriptors are either in - * process or have not been submitted - */ - if (seen_current) - break; + /* clean finished descriptors */ + hw_desc = iter->hw_desc; + if (hw_desc->status & XOR_DESC_SUCCESS) { + cookie = mv_xor_run_tx_complete_actions(iter, mv_chan, + cookie); - /* stop the search if we reach the current descriptor and the - * channel is busy - */ - if (iter->async_tx.phys == current_desc) { - seen_current = 1; - if (busy) + /* done processing desc, clean slot */ + mv_xor_clean_slot(iter, mv_chan); + + /* break if we did cleaned the current */ + if (iter->async_tx.phys == current_desc) { + current_cleaned = 1; + break; + } + } else { + if (iter->async_tx.phys == current_desc) { + current_cleaned = 0; break; + } } - - cookie = mv_xor_run_tx_complete_actions(iter, mv_chan, cookie); - - if (mv_xor_clean_slot(iter, mv_chan)) - break; } if ((busy == 0) && !list_empty(&mv_chan->chain)) { - struct mv_xor_desc_slot *chain_head; - chain_head = list_entry(mv_chan->chain.next, - struct mv_xor_desc_slot, - chain_node); - - mv_xor_start_new_chain(mv_chan, chain_head); + if (current_cleaned) { + /* + * current descriptor cleaned and removed, run + * from list head + */ + iter = list_entry(mv_chan->chain.next, + struct mv_xor_desc_slot, + chain_node); + mv_xor_start_new_chain(mv_chan, iter); + } else { + if (!list_is_last(&iter->chain_node, &mv_chan->chain)) { + /* + * descriptors are still waiting after + * current, trigger them + */ + iter = list_entry(iter->chain_node.next, + struct mv_xor_desc_slot, + chain_node); + mv_xor_start_new_chain(mv_chan, iter); + } else { + /* + * some descriptors are still waiting + * to be cleaned + */ + tasklet_schedule(&mv_chan->irq_tasklet); + } + } } if (cookie > 0) @@ -1305,7 +1323,7 @@ static int mv_xor_shared_probe(struct platform_device *pdev) return -ENODEV; msp->xor_base = devm_ioremap(&pdev->dev, res->start, - res->end - res->start + 1); + resource_size(res)); if (!msp->xor_base) return -EBUSY; @@ -1314,7 +1332,7 @@ static int mv_xor_shared_probe(struct platform_device *pdev) return -ENODEV; msp->xor_high_base = devm_ioremap(&pdev->dev, res->start, - res->end - res->start + 1); + resource_size(res)); if (!msp->xor_high_base) return -EBUSY; diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index 977b592..ae2cfba 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -30,6 +30,7 @@ #define XOR_OPERATION_MODE_XOR 0 #define XOR_OPERATION_MODE_MEMCPY 2 #define XOR_OPERATION_MODE_MEMSET 4 +#define XOR_DESC_SUCCESS 0x40000000 #define XOR_CURR_DESC(chan) (chan->mmr_base + 0x210 + (chan->idx * 4)) #define XOR_NEXT_DESC(chan) (chan->mmr_base + 0x200 + (chan->idx * 4)) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 88aad4f..b4588bd 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -130,6 +130,23 @@ struct mxs_dma_engine { struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; }; +static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable) +{ + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; + int chan_id = mxs_chan->chan.chan_id; + int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR; + + /* enable apbh channel clock */ + if (dma_is_apbh()) { + if (apbh_is_old()) + writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL), + mxs_dma->base + HW_APBHX_CTRL0 + set_clr); + else + writel(1 << chan_id, + mxs_dma->base + HW_APBHX_CTRL0 + set_clr); + } +} + static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) { struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -148,38 +165,21 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; + /* clkgate needs to be enabled before writing other registers */ + mxs_dma_clkgate(mxs_chan, 1); + /* set cmd_addr up */ writel(mxs_chan->ccw_phys, mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); - /* enable apbh channel clock */ - if (dma_is_apbh()) { - if (apbh_is_old()) - writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL), - mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR); - else - writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR); - } - /* write 1 to SEMA to kick off the channel */ writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id)); } static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) { - struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - int chan_id = mxs_chan->chan.chan_id; - /* disable apbh channel clock */ - if (dma_is_apbh()) { - if (apbh_is_old()) - writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL), - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); - else - writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); - } + mxs_dma_clkgate(mxs_chan, 0); mxs_chan->status = DMA_SUCCESS; } @@ -327,16 +327,21 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) memset(mxs_chan->ccw, 0, PAGE_SIZE); - ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler, - 0, "mxs-dma", mxs_dma); - if (ret) - goto err_irq; + if (mxs_chan->chan_irq != NO_IRQ) { + ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler, + 0, "mxs-dma", mxs_dma); + if (ret) + goto err_irq; + } ret = clk_enable(mxs_dma->clk); if (ret) goto err_clk; + /* clkgate needs to be enabled for reset to finish */ + mxs_dma_clkgate(mxs_chan, 1); mxs_dma_reset_chan(mxs_chan); + mxs_dma_clkgate(mxs_chan, 0); dma_async_tx_descriptor_init(&mxs_chan->desc, chan); mxs_chan->desc.tx_submit = mxs_dma_tx_submit; @@ -535,6 +540,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: mxs_dma_disable_chan(mxs_chan); + mxs_dma_reset_chan(mxs_chan); break; case DMA_PAUSE: mxs_dma_pause_chan(mxs_chan); @@ -707,6 +713,8 @@ static struct platform_device_id mxs_dma_type[] = { }, { .name = "mxs-dma-apbx", .driver_data = MXS_DMA_APBX, + }, { + /* end of list */ } }; diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index e6f128a..49138e7 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -872,8 +872,7 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, int i; nr_channels = id->driver_data; - pd = kzalloc(sizeof(struct pch_dma)+ - sizeof(struct pch_dma_chan) * nr_channels, GFP_KERNEL); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) return -ENOMEM; @@ -926,7 +925,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, } pd->dma.dev = &pdev->dev; - pd->dma.chancnt = nr_channels; INIT_LIST_HEAD(&pd->dma.channels); @@ -935,7 +933,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, pd_chan->chan.device = &pd->dma; pd_chan->chan.cookie = 1; - pd_chan->chan.chan_id = i; pd_chan->membase = ®s->desc[i]; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 4802aac..720cace 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #define NR_DEFAULT_DESC 16 @@ -68,6 +70,14 @@ struct dma_pl330_chan { * NULL if the channel is available to be acquired. */ void *pl330_chid; + + /* For D-to-M and M-to-D channels */ + int burst_sz; /* the peripheral fifo width */ + int burst_len; /* the number of burst */ + dma_addr_t fifo_addr; + + /* for cyclic capability */ + bool cyclic; }; struct dma_pl330_dmac { @@ -82,7 +92,9 @@ struct dma_pl330_dmac { spinlock_t pool_lock; /* Peripheral channels connected to this DMAC */ - struct dma_pl330_chan peripherals[0]; /* keep at end */ + struct dma_pl330_chan *peripherals; /* keep at end */ + + struct clk *clk; }; struct dma_pl330_desc { @@ -152,6 +164,31 @@ static inline void free_desc_list(struct list_head *list) spin_unlock_irqrestore(&pdmac->pool_lock, flags); } +static inline void handle_cyclic_desc_list(struct list_head *list) +{ + struct dma_pl330_desc *desc; + struct dma_pl330_chan *pch; + unsigned long flags; + + if (list_empty(list)) + return; + + list_for_each_entry(desc, list, node) { + dma_async_tx_callback callback; + + /* Change status to reload it */ + desc->status = PREP; + pch = desc->pchan; + callback = desc->txd.callback; + if (callback) + callback(desc->txd.callback_param); + } + + spin_lock_irqsave(&pch->lock, flags); + list_splice_tail_init(list, &pch->work_list); + spin_unlock_irqrestore(&pch->lock, flags); +} + static inline void fill_queue(struct dma_pl330_chan *pch) { struct dma_pl330_desc *desc; @@ -205,7 +242,10 @@ static void pl330_tasklet(unsigned long data) spin_unlock_irqrestore(&pch->lock, flags); - free_desc_list(&list); + if (pch->cyclic) + handle_cyclic_desc_list(&list); + else + free_desc_list(&list); } static void dma_pl330_rqcb(void *token, enum pl330_op_err err) @@ -236,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&pch->lock, flags); pch->completed = chan->cookie = 1; + pch->cyclic = false; pch->pl330_chid = pl330_request_channel(&pdmac->pif); if (!pch->pl330_chid) { @@ -253,25 +294,52 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct dma_pl330_chan *pch = to_pchan(chan); - struct dma_pl330_desc *desc; + struct dma_pl330_desc *desc, *_dt; unsigned long flags; + struct dma_pl330_dmac *pdmac = pch->dmac; + struct dma_slave_config *slave_config; + LIST_HEAD(list); - /* Only supports DMA_TERMINATE_ALL */ - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; - - spin_lock_irqsave(&pch->lock, flags); - - /* FLUSH the PL330 Channel thread */ - pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); + switch (cmd) { + case DMA_TERMINATE_ALL: + spin_lock_irqsave(&pch->lock, flags); - /* Mark all desc done */ - list_for_each_entry(desc, &pch->work_list, node) - desc->status = DONE; + /* FLUSH the PL330 Channel thread */ + pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); - spin_unlock_irqrestore(&pch->lock, flags); + /* Mark all desc done */ + list_for_each_entry_safe(desc, _dt, &pch->work_list , node) { + desc->status = DONE; + pch->completed = desc->txd.cookie; + list_move_tail(&desc->node, &list); + } - pl330_tasklet((unsigned long) pch); + list_splice_tail_init(&list, &pdmac->desc_pool); + spin_unlock_irqrestore(&pch->lock, flags); + break; + case DMA_SLAVE_CONFIG: + slave_config = (struct dma_slave_config *)arg; + + if (slave_config->direction == DMA_TO_DEVICE) { + if (slave_config->dst_addr) + pch->fifo_addr = slave_config->dst_addr; + if (slave_config->dst_addr_width) + pch->burst_sz = __ffs(slave_config->dst_addr_width); + if (slave_config->dst_maxburst) + pch->burst_len = slave_config->dst_maxburst; + } else if (slave_config->direction == DMA_FROM_DEVICE) { + if (slave_config->src_addr) + pch->fifo_addr = slave_config->src_addr; + if (slave_config->src_addr_width) + pch->burst_sz = __ffs(slave_config->src_addr_width); + if (slave_config->src_maxburst) + pch->burst_len = slave_config->src_maxburst; + } + break; + default: + dev_err(pch->dmac->pif.dev, "Not supported command.\n"); + return -ENXIO; + } return 0; } @@ -288,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan) pl330_release_channel(pch->pl330_chid); pch->pl330_chid = NULL; + if (pch->cyclic) + list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + spin_unlock_irqrestore(&pch->lock, flags); } @@ -451,8 +522,13 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) desc->txd.cookie = 0; async_tx_ack(&desc->txd); - desc->req.rqtype = peri->rqtype; - desc->req.peri = peri->peri_id; + if (peri) { + desc->req.rqtype = peri->rqtype; + desc->req.peri = pch->chan.chan_id; + } else { + desc->req.rqtype = MEMTOMEM; + desc->req.peri = 0; + } dma_async_tx_descriptor_init(&desc->txd, &pch->chan); @@ -519,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) return burst_len; } +static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t dma_addr, size_t len, + size_t period_len, enum dma_data_direction direction) +{ + struct dma_pl330_desc *desc; + struct dma_pl330_chan *pch = to_pchan(chan); + dma_addr_t dst; + dma_addr_t src; + + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n", + __func__, __LINE__); + return NULL; + } + + switch (direction) { + case DMA_TO_DEVICE: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + src = dma_addr; + dst = pch->fifo_addr; + break; + case DMA_FROM_DEVICE: + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + src = pch->fifo_addr; + dst = dma_addr; + break; + default: + dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n", + __func__, __LINE__); + return NULL; + } + + desc->rqcfg.brst_size = pch->burst_sz; + desc->rqcfg.brst_len = 1; + + pch->cyclic = true; + + fill_px(&desc->px, dst, src, period_len); + + return &desc->txd; +} + static struct dma_async_tx_descriptor * pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, size_t len, unsigned long flags) @@ -529,10 +650,10 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, struct pl330_info *pi; int burst; - if (unlikely(!pch || !len || !peri)) + if (unlikely(!pch || !len)) return NULL; - if (peri->rqtype != MEMTOMEM) + if (peri && peri->rqtype != MEMTOMEM) return NULL; pi = &pch->dmac->pif; @@ -574,10 +695,10 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dma_pl330_peri *peri = chan->private; struct scatterlist *sg; unsigned long flags; - int i, burst_size; + int i; dma_addr_t addr; - if (unlikely(!pch || !sgl || !sg_len)) + if (unlikely(!pch || !sgl || !sg_len || !peri)) return NULL; /* Make sure the direction is consistent */ @@ -590,8 +711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } - addr = peri->fifo_addr; - burst_size = peri->burst_sz; + addr = pch->fifo_addr; first = NULL; @@ -639,7 +759,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, sg_dma_address(sg), addr, sg_dma_len(sg)); } - desc->rqcfg.brst_size = burst_size; + desc->rqcfg.brst_size = pch->burst_sz; desc->rqcfg.brst_len = 1; } @@ -666,17 +786,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) struct dma_device *pd; struct resource *res; int i, ret, irq; + int num_chan; pdat = adev->dev.platform_data; - if (!pdat || !pdat->nr_valid_peri) { - dev_err(&adev->dev, "platform data missing\n"); - return -ENODEV; - } - /* Allocate a new DMAC and its Channels */ - pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch) - + sizeof(*pdmac), GFP_KERNEL); + pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL); if (!pdmac) { dev_err(&adev->dev, "unable to allocate mem\n"); return -ENOMEM; @@ -685,7 +800,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi = &pdmac->pif; pi->dev = &adev->dev; pi->pl330_data = NULL; - pi->mcbufsz = pdat->mcbuf_sz; + pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0; res = &adev->res; request_mem_region(res->start, resource_size(res), "dma-pl330"); @@ -696,6 +811,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) goto probe_err1; } + pdmac->clk = clk_get(&adev->dev, "dma"); + if (IS_ERR(pdmac->clk)) { + dev_err(&adev->dev, "Cannot get operation clock.\n"); + ret = -EINVAL; + goto probe_err1; + } + + amba_set_drvdata(adev, pdmac); + +#ifdef CONFIG_PM_RUNTIME + /* to use the runtime PM helper functions */ + pm_runtime_enable(&adev->dev); + + /* enable the power domain */ + if (pm_runtime_get_sync(&adev->dev)) { + dev_err(&adev->dev, "failed to get runtime pm\n"); + ret = -ENODEV; + goto probe_err1; + } +#else + /* enable dma clk */ + clk_enable(pdmac->clk); +#endif + irq = adev->irq[0]; ret = request_irq(irq, pl330_irq_handler, 0, dev_name(&adev->dev), pi); @@ -717,33 +856,45 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) INIT_LIST_HEAD(&pd->channels); /* Initialize channel parameters */ - for (i = 0; i < pdat->nr_valid_peri; i++) { - struct dma_pl330_peri *peri = &pdat->peri[i]; - pch = &pdmac->peripherals[i]; + num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan); + pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL); + if (!pdmac->peripherals) { + ret = -ENOMEM; + dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n"); + goto probe_err4; + } - switch (peri->rqtype) { - case MEMTOMEM: + for (i = 0; i < num_chan; i++) { + pch = &pdmac->peripherals[i]; + if (pdat) { + struct dma_pl330_peri *peri = &pdat->peri[i]; + + switch (peri->rqtype) { + case MEMTOMEM: + dma_cap_set(DMA_MEMCPY, pd->cap_mask); + break; + case MEMTODEV: + case DEVTOMEM: + dma_cap_set(DMA_SLAVE, pd->cap_mask); + dma_cap_set(DMA_CYCLIC, pd->cap_mask); + break; + default: + dev_err(&adev->dev, "DEVTODEV Not Supported\n"); + continue; + } + pch->chan.private = peri; + } else { dma_cap_set(DMA_MEMCPY, pd->cap_mask); - break; - case MEMTODEV: - case DEVTOMEM: - dma_cap_set(DMA_SLAVE, pd->cap_mask); - break; - default: - dev_err(&adev->dev, "DEVTODEV Not Supported\n"); - continue; + pch->chan.private = NULL; } INIT_LIST_HEAD(&pch->work_list); spin_lock_init(&pch->lock); pch->pl330_chid = NULL; - pch->chan.private = peri; pch->chan.device = pd; - pch->chan.chan_id = i; pch->dmac = pdmac; /* Add the channel to the DMAC list */ - pd->chancnt++; list_add_tail(&pch->chan.device_node, &pd->channels); } @@ -752,6 +903,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; + pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_control = pl330_control; @@ -763,8 +915,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) goto probe_err4; } - amba_set_drvdata(adev, pdmac); - dev_info(&adev->dev, "Loaded driver for PL330 DMAC-%d\n", adev->periphid); dev_info(&adev->dev, @@ -825,6 +975,13 @@ static int __devexit pl330_remove(struct amba_device *adev) res = &adev->res; release_mem_region(res->start, resource_size(res)); +#ifdef CONFIG_PM_RUNTIME + pm_runtime_put(&adev->dev); + pm_runtime_disable(&adev->dev); +#else + clk_disable(pdmac->clk); +#endif + kfree(pdmac); return 0; @@ -838,10 +995,49 @@ static struct amba_id pl330_ids[] = { { 0, 0 }, }; +#ifdef CONFIG_PM_RUNTIME +static int pl330_runtime_suspend(struct device *dev) +{ + struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev); + + if (!pdmac) { + dev_err(dev, "failed to get dmac\n"); + return -ENODEV; + } + + clk_disable(pdmac->clk); + + return 0; +} + +static int pl330_runtime_resume(struct device *dev) +{ + struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev); + + if (!pdmac) { + dev_err(dev, "failed to get dmac\n"); + return -ENODEV; + } + + clk_enable(pdmac->clk); + + return 0; +} +#else +#define pl330_runtime_suspend NULL +#define pl330_runtime_resume NULL +#endif /* CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops pl330_pm_ops = { + .runtime_suspend = pl330_runtime_suspend, + .runtime_resume = pl330_runtime_resume, +}; + static struct amba_driver pl330_driver = { .drv = { .owner = THIS_MODULE, .name = "dma-pl330", + .pm = &pl330_pm_ops, }, .id_table = pl330_ids, .probe = pl330_probe, diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 0283300..81809c2 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -70,12 +70,36 @@ static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg) static u16 dmaor_read(struct sh_dmae_device *shdev) { - return __raw_readw(shdev->chan_reg + DMAOR / sizeof(u32)); + u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32); + + if (shdev->pdata->dmaor_is_32bit) + return __raw_readl(addr); + else + return __raw_readw(addr); } static void dmaor_write(struct sh_dmae_device *shdev, u16 data) { - __raw_writew(data, shdev->chan_reg + DMAOR / sizeof(u32)); + u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32); + + if (shdev->pdata->dmaor_is_32bit) + __raw_writel(data, addr); + else + __raw_writew(data, addr); +} + +static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + __raw_writel(data, sh_dc->base + shdev->chcr_offset / sizeof(u32)); +} + +static u32 chcr_read(struct sh_dmae_chan *sh_dc) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + return __raw_readl(sh_dc->base + shdev->chcr_offset / sizeof(u32)); } /* @@ -120,7 +144,7 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev) static bool dmae_is_busy(struct sh_dmae_chan *sh_chan) { - u32 chcr = sh_dmae_readl(sh_chan, CHCR); + u32 chcr = chcr_read(sh_chan); if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE) return true; /* working */ @@ -130,8 +154,7 @@ static bool dmae_is_busy(struct sh_dmae_chan *sh_chan) static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr) { - struct sh_dmae_device *shdev = container_of(sh_chan->common.device, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) | ((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift); @@ -144,8 +167,7 @@ static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr) static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size) { - struct sh_dmae_device *shdev = container_of(sh_chan->common.device, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; int i; @@ -169,18 +191,23 @@ static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw) static void dmae_start(struct sh_dmae_chan *sh_chan) { - u32 chcr = sh_dmae_readl(sh_chan, CHCR); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + u32 chcr = chcr_read(sh_chan); + + if (shdev->pdata->needs_tend_set) + sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND); - chcr |= CHCR_DE | CHCR_IE; - sh_dmae_writel(sh_chan, chcr & ~CHCR_TE, CHCR); + chcr |= CHCR_DE | shdev->chcr_ie_bit; + chcr_write(sh_chan, chcr & ~CHCR_TE); } static void dmae_halt(struct sh_dmae_chan *sh_chan) { - u32 chcr = sh_dmae_readl(sh_chan, CHCR); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + u32 chcr = chcr_read(sh_chan); - chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); - sh_dmae_writel(sh_chan, chcr, CHCR); + chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit); + chcr_write(sh_chan, chcr); } static void dmae_init(struct sh_dmae_chan *sh_chan) @@ -192,7 +219,7 @@ static void dmae_init(struct sh_dmae_chan *sh_chan) u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan, LOG2_DEFAULT_XFER_SIZE); sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr); - sh_dmae_writel(sh_chan, chcr, CHCR); + chcr_write(sh_chan, chcr); } static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) @@ -202,23 +229,25 @@ static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) return -EBUSY; sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val); - sh_dmae_writel(sh_chan, val, CHCR); + chcr_write(sh_chan, val); return 0; } static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) { - struct sh_dmae_device *shdev = container_of(sh_chan->common.device, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; u16 __iomem *addr = shdev->dmars; - int shift = chan_pdata->dmars_bit; + unsigned int shift = chan_pdata->dmars_bit; if (dmae_is_busy(sh_chan)) return -EBUSY; + if (pdata->no_dmars) + return 0; + /* in the case of a missing DMARS resource use first memory window */ if (!addr) addr = (u16 __iomem *)shdev->chan_reg; @@ -230,14 +259,23 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) return 0; } +static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan); + static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) { struct sh_desc *desc = tx_to_sh_desc(tx), *chunk, *last = desc, *c; struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan); + struct sh_dmae_slave *param = tx->chan->private; dma_async_tx_callback callback = tx->callback; dma_cookie_t cookie; + bool power_up; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irq(&sh_chan->desc_lock); + + if (list_empty(&sh_chan->ld_queue)) + power_up = true; + else + power_up = false; cookie = sh_chan->common.cookie; cookie++; @@ -273,7 +311,38 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) tx->cookie, &last->async_tx, sh_chan->id, desc->hw.sar, desc->hw.tcr, desc->hw.dar); - spin_unlock_bh(&sh_chan->desc_lock); + if (power_up) { + sh_chan->pm_state = DMAE_PM_BUSY; + + pm_runtime_get(sh_chan->dev); + + spin_unlock_irq(&sh_chan->desc_lock); + + pm_runtime_barrier(sh_chan->dev); + + spin_lock_irq(&sh_chan->desc_lock); + + /* Have we been reset, while waiting? */ + if (sh_chan->pm_state != DMAE_PM_ESTABLISHED) { + dev_dbg(sh_chan->dev, "Bring up channel %d\n", + sh_chan->id); + if (param) { + const struct sh_dmae_slave_config *cfg = + param->config; + + dmae_set_dmars(sh_chan, cfg->mid_rid); + dmae_set_chcr(sh_chan, cfg->chcr); + } else { + dmae_init(sh_chan); + } + + if (sh_chan->pm_state == DMAE_PM_PENDING) + sh_chan_xfer_ld_queue(sh_chan); + sh_chan->pm_state = DMAE_PM_ESTABLISHED; + } + } + + spin_unlock_irq(&sh_chan->desc_lock); return cookie; } @@ -296,9 +365,7 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan) static const struct sh_dmae_slave_config *sh_dmae_find_slave( struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param) { - struct dma_device *dma_dev = sh_chan->common.device; - struct sh_dmae_device *shdev = container_of(dma_dev, - struct sh_dmae_device, common); + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; int i; @@ -319,8 +386,6 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) struct sh_dmae_slave *param = chan->private; int ret; - pm_runtime_get_sync(sh_chan->dev); - /* * This relies on the guarantee from dmaengine that alloc_chan_resources * never runs concurrently with itself or free_chan_resources. @@ -340,31 +405,20 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) } param->config = cfg; - - dmae_set_dmars(sh_chan, cfg->mid_rid); - dmae_set_chcr(sh_chan, cfg->chcr); - } else { - dmae_init(sh_chan); } - spin_lock_bh(&sh_chan->desc_lock); while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) { - spin_unlock_bh(&sh_chan->desc_lock); desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL); - if (!desc) { - spin_lock_bh(&sh_chan->desc_lock); + if (!desc) break; - } dma_async_tx_descriptor_init(&desc->async_tx, &sh_chan->common); desc->async_tx.tx_submit = sh_dmae_tx_submit; desc->mark = DESC_IDLE; - spin_lock_bh(&sh_chan->desc_lock); list_add(&desc->node, &sh_chan->ld_free); sh_chan->descs_allocated++; } - spin_unlock_bh(&sh_chan->desc_lock); if (!sh_chan->descs_allocated) { ret = -ENOMEM; @@ -378,7 +432,7 @@ edescalloc: clear_bit(param->slave_id, sh_dmae_slave_used); etestused: efindslave: - pm_runtime_put(sh_chan->dev); + chan->private = NULL; return ret; } @@ -390,7 +444,6 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan) struct sh_dmae_chan *sh_chan = to_sh_chan(chan); struct sh_desc *desc, *_desc; LIST_HEAD(list); - int descs = sh_chan->descs_allocated; /* Protect against ISR */ spin_lock_irq(&sh_chan->desc_lock); @@ -410,15 +463,12 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan) chan->private = NULL; } - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irq(&sh_chan->desc_lock); list_splice_init(&sh_chan->ld_free, &list); sh_chan->descs_allocated = 0; - spin_unlock_bh(&sh_chan->desc_lock); - - if (descs > 0) - pm_runtime_put(sh_chan->dev); + spin_unlock_irq(&sh_chan->desc_lock); list_for_each_entry_safe(desc, _desc, &list, node) kfree(desc); @@ -507,6 +557,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c struct sh_desc *first = NULL, *new = NULL /* compiler... */; LIST_HEAD(tx_list); int chunks = 0; + unsigned long irq_flags; int i; if (!sg_len) @@ -517,7 +568,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c (SH_DMA_TCR_MAX + 1); /* Have to lock the whole loop to protect against concurrent release */ - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, irq_flags); /* * Chaining: @@ -563,7 +614,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c /* Put them back on the free list, so, they don't get lost */ list_splice_tail(&tx_list, &sh_chan->ld_free); - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags); return &first->async_tx; @@ -572,7 +623,7 @@ err_get_desc: new->mark = DESC_IDLE; list_splice(&tx_list, &sh_chan->ld_free); - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags); return NULL; } @@ -634,6 +685,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + unsigned long flags; /* Only supports DMA_TERMINATE_ALL */ if (cmd != DMA_TERMINATE_ALL) @@ -642,7 +694,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, if (!chan) return -EINVAL; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); dmae_halt(sh_chan); if (!list_empty(&sh_chan->ld_queue)) { @@ -651,9 +703,8 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct sh_desc, node); desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) << sh_chan->xmit_shift; - } - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); sh_dmae_chan_ld_cleanup(sh_chan, true); @@ -668,8 +719,9 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all dma_cookie_t cookie = 0; dma_async_tx_callback callback = NULL; void *param = NULL; + unsigned long flags; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) { struct dma_async_tx_descriptor *tx = &desc->async_tx; @@ -735,7 +787,13 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all async_tx_test_ack(&desc->async_tx)) || all) { /* Remove from ld_queue list */ desc->mark = DESC_IDLE; + list_move(&desc->node, &sh_chan->ld_free); + + if (list_empty(&sh_chan->ld_queue)) { + dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id); + pm_runtime_put(sh_chan->dev); + } } } @@ -746,7 +804,7 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all */ sh_chan->completed_cookie = sh_chan->common.cookie; - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); if (callback) callback(param); @@ -765,16 +823,14 @@ static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all) ; } +/* Called under spin_lock_irq(&sh_chan->desc_lock) */ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) { struct sh_desc *desc; - spin_lock_bh(&sh_chan->desc_lock); /* DMA work check */ - if (dmae_is_busy(sh_chan)) { - spin_unlock_bh(&sh_chan->desc_lock); + if (dmae_is_busy(sh_chan)) return; - } /* Find the first not transferred descriptor */ list_for_each_entry(desc, &sh_chan->ld_queue, node) @@ -787,14 +843,18 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) dmae_start(sh_chan); break; } - - spin_unlock_bh(&sh_chan->desc_lock); } static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); - sh_chan_xfer_ld_queue(sh_chan); + + spin_lock_irq(&sh_chan->desc_lock); + if (sh_chan->pm_state == DMAE_PM_ESTABLISHED) + sh_chan_xfer_ld_queue(sh_chan); + else + sh_chan->pm_state = DMAE_PM_PENDING; + spin_unlock_irq(&sh_chan->desc_lock); } static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, @@ -805,6 +865,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, dma_cookie_t last_used; dma_cookie_t last_complete; enum dma_status status; + unsigned long flags; sh_dmae_chan_ld_cleanup(sh_chan, false); @@ -815,7 +876,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, BUG_ON(last_complete < 0); dma_set_tx_state(txstate, last_complete, last_used, 0); - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); status = dma_async_is_complete(cookie, last_complete, last_used); @@ -833,7 +894,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, } } - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); return status; } @@ -846,7 +907,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data) spin_lock(&sh_chan->desc_lock); - chcr = sh_dmae_readl(sh_chan, CHCR); + chcr = chcr_read(sh_chan); if (chcr & CHCR_TE) { /* DMA stop */ @@ -886,6 +947,12 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev) list_splice_init(&sh_chan->ld_queue, &dl); + if (!list_empty(&dl)) { + dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id); + pm_runtime_put(sh_chan->dev); + } + sh_chan->pm_state = DMAE_PM_ESTABLISHED; + spin_unlock(&sh_chan->desc_lock); /* Complete all */ @@ -926,7 +993,7 @@ static void dmae_do_tasklet(unsigned long data) u32 sar_buf = sh_dmae_readl(sh_chan, SAR); u32 dar_buf = sh_dmae_readl(sh_chan, DAR); - spin_lock(&sh_chan->desc_lock); + spin_lock_irq(&sh_chan->desc_lock); list_for_each_entry(desc, &sh_chan->ld_queue, node) { if (desc->mark == DESC_SUBMITTED && ((desc->direction == DMA_FROM_DEVICE && @@ -939,10 +1006,10 @@ static void dmae_do_tasklet(unsigned long data) break; } } - spin_unlock(&sh_chan->desc_lock); - /* Next desc */ sh_chan_xfer_ld_queue(sh_chan); + spin_unlock_irq(&sh_chan->desc_lock); + sh_dmae_chan_ld_cleanup(sh_chan, false); } @@ -1010,7 +1077,9 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, return -ENOMEM; } - /* copy struct dma_device */ + new_sh_chan->pm_state = DMAE_PM_ESTABLISHED; + + /* reference struct dma_device */ new_sh_chan->common.device = &shdev->common; new_sh_chan->dev = shdev->common.dev; @@ -1144,6 +1213,16 @@ static int __init sh_dmae_probe(struct platform_device *pdev) /* platform data */ shdev->pdata = pdata; + if (pdata->chcr_offset) + shdev->chcr_offset = pdata->chcr_offset; + else + shdev->chcr_offset = CHCR; + + if (pdata->chcr_ie_bit) + shdev->chcr_ie_bit = pdata->chcr_ie_bit; + else + shdev->chcr_ie_bit = CHCR_IE; + platform_set_drvdata(pdev, shdev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 5ae9fc5..2b55a27 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -23,6 +23,12 @@ struct device; +enum dmae_pm_state { + DMAE_PM_ESTABLISHED, + DMAE_PM_BUSY, + DMAE_PM_PENDING, +}; + struct sh_dmae_chan { dma_cookie_t completed_cookie; /* The maximum cookie completed */ spinlock_t desc_lock; /* Descriptor operation lock */ @@ -38,6 +44,7 @@ struct sh_dmae_chan { u32 __iomem *base; char dev_id[16]; /* unique name per DMAC of channel */ int pm_error; + enum dmae_pm_state pm_state; }; struct sh_dmae_device { @@ -47,10 +54,14 @@ struct sh_dmae_device { struct list_head node; u32 __iomem *chan_reg; u16 __iomem *dmars; + unsigned int chcr_offset; + u32 chcr_ie_bit; }; #define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common) #define to_sh_desc(lh) container_of(lh, struct sh_desc, node) #define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx) +#define to_sh_dev(chan) container_of(chan->common.device,\ + struct sh_dmae_device, common) #endif /* __DMA_SHDMA_H */ diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 8f222d4..4b27c54 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -6,13 +6,16 @@ * License terms: GNU General Public License (GPL) version 2 */ +#include #include #include +#include #include #include #include #include #include +#include #include @@ -44,9 +47,6 @@ #define D40_ALLOC_PHY (1 << 30) #define D40_ALLOC_LOG_FREE 0 -/* Hardware designer of the block */ -#define D40_HW_DESIGNER 0x8 - /** * enum 40_command - The different commands and/or statuses. * @@ -175,8 +175,10 @@ struct d40_base; * @tasklet: Tasklet that gets scheduled from interrupt context to complete a * transfer and call client callback. * @client: Cliented owned descriptor list. + * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. * @queue: Queued jobs. + * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. * @configured: whether the dma_cfg configuration is valid * @base: Pointer to the device instance struct. @@ -185,6 +187,8 @@ struct d40_base; * @log_def: Default logical channel settings. * @lcla: Space for one dst src pair for logical channel transfers. * @lcpa: Pointer to dst and src lcpa settings. + * @runtime_addr: runtime configured address. + * @runtime_direction: runtime configured direction. * * This struct can either "be" a logical or a physical channel. */ @@ -199,8 +203,10 @@ struct d40_chan { struct dma_chan chan; struct tasklet_struct tasklet; struct list_head client; + struct list_head pending_queue; struct list_head active; struct list_head queue; + struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; bool configured; struct d40_base *base; @@ -475,7 +481,6 @@ static struct d40_desc *d40_desc_get(struct d40_chan *d40c) list_for_each_entry_safe(d, _d, &d40c->client, node) if (async_tx_test_ack(&d->txd)) { - d40_pool_lli_free(d40c, d); d40_desc_remove(d); desc = d; memset(desc, 0, sizeof(*desc)); @@ -642,9 +647,25 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c) return d; } +/* remove desc from current queue and add it to the pending_queue */ static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc) { - list_add_tail(&desc->node, &d40c->queue); + d40_desc_remove(desc); + desc->is_in_client_list = false; + list_add_tail(&desc->node, &d40c->pending_queue); +} + +static struct d40_desc *d40_first_pending(struct d40_chan *d40c) +{ + struct d40_desc *d; + + if (list_empty(&d40c->pending_queue)) + return NULL; + + d = list_first_entry(&d40c->pending_queue, + struct d40_desc, + node); + return d; } static struct d40_desc *d40_first_queued(struct d40_chan *d40c) @@ -788,6 +809,7 @@ done: static void d40_term_all(struct d40_chan *d40c) { struct d40_desc *d40d; + struct d40_desc *_d; /* Release active descriptors */ while ((d40d = d40_first_active_get(d40c))) { @@ -801,6 +823,26 @@ static void d40_term_all(struct d40_chan *d40c) d40_desc_free(d40c, d40d); } + /* Release pending descriptors */ + while ((d40d = d40_first_pending(d40c))) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } + + /* Release client owned descriptors */ + if (!list_empty(&d40c->client)) + list_for_each_entry_safe(d40d, _d, &d40c->client, node) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } + + /* Release descriptors in prepare queue */ + if (!list_empty(&d40c->prepare_queue)) + list_for_each_entry_safe(d40d, _d, + &d40c->prepare_queue, node) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } d40c->pending_tx = 0; d40c->busy = false; @@ -1160,6 +1202,7 @@ static void dma_tasklet(unsigned long data) struct d40_chan *d40c = (struct d40_chan *) data; struct d40_desc *d40d; unsigned long flags; + bool callback_active; dma_async_tx_callback callback; void *callback_param; @@ -1183,12 +1226,12 @@ static void dma_tasklet(unsigned long data) } /* Callback to client */ + callback_active = !!(d40d->txd.flags & DMA_PREP_INTERRUPT); callback = d40d->txd.callback; callback_param = d40d->txd.callback_param; if (!d40d->cyclic) { if (async_tx_test_ack(&d40d->txd)) { - d40_pool_lli_free(d40c, d40d); d40_desc_remove(d40d); d40_desc_free(d40c, d40d); } else { @@ -1208,7 +1251,7 @@ static void dma_tasklet(unsigned long data) spin_unlock_irqrestore(&d40c->lock, flags); - if (callback && (d40d->txd.flags & DMA_PREP_INTERRUPT)) + if (callback_active && callback) callback(callback_param); return; @@ -1575,21 +1618,10 @@ static int d40_free_dma(struct d40_chan *d40c) u32 event; struct d40_phy_res *phy = d40c->phy_chan; bool is_src; - struct d40_desc *d; - struct d40_desc *_d; - /* Terminate all queued and active transfers */ d40_term_all(d40c); - /* Release client owned descriptors */ - if (!list_empty(&d40c->client)) - list_for_each_entry_safe(d, _d, &d40c->client, node) { - d40_pool_lli_free(d40c, d); - d40_desc_remove(d); - d40_desc_free(d40c, d); - } - if (phy == NULL) { chan_err(d40c, "phy == null\n"); return -EINVAL; @@ -1891,6 +1923,12 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, goto err; } + /* + * add descriptor to the prepare queue in order to be able + * to free them later in terminate_all + */ + list_add_tail(&desc->node, &chan->prepare_queue); + spin_unlock_irqrestore(&chan->lock, flags); return &desc->txd; @@ -2091,7 +2129,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, struct scatterlist *sg; int i; - sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_KERNEL); + sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT); for (i = 0; i < periods; i++) { sg_dma_address(&sg[i]) = dma_addr; sg_dma_len(&sg[i]) = period_len; @@ -2151,24 +2189,87 @@ static void d40_issue_pending(struct dma_chan *chan) spin_lock_irqsave(&d40c->lock, flags); - /* Busy means that pending jobs are already being processed */ + list_splice_tail_init(&d40c->pending_queue, &d40c->queue); + + /* Busy means that queued jobs are already being processed */ if (!d40c->busy) (void) d40_queue_start(d40c); spin_unlock_irqrestore(&d40c->lock, flags); } +static int +dma40_config_to_halfchannel(struct d40_chan *d40c, + struct stedma40_half_channel_info *info, + enum dma_slave_buswidth width, + u32 maxburst) +{ + enum stedma40_periph_data_width addr_width; + int psize; + + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + addr_width = STEDMA40_BYTE_WIDTH; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + addr_width = STEDMA40_HALFWORD_WIDTH; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + addr_width = STEDMA40_WORD_WIDTH; + break; + case DMA_SLAVE_BUSWIDTH_8_BYTES: + addr_width = STEDMA40_DOUBLEWORD_WIDTH; + break; + default: + dev_err(d40c->base->dev, + "illegal peripheral address width " + "requested (%d)\n", + width); + return -EINVAL; + } + + if (chan_is_logical(d40c)) { + if (maxburst >= 16) + psize = STEDMA40_PSIZE_LOG_16; + else if (maxburst >= 8) + psize = STEDMA40_PSIZE_LOG_8; + else if (maxburst >= 4) + psize = STEDMA40_PSIZE_LOG_4; + else + psize = STEDMA40_PSIZE_LOG_1; + } else { + if (maxburst >= 16) + psize = STEDMA40_PSIZE_PHY_16; + else if (maxburst >= 8) + psize = STEDMA40_PSIZE_PHY_8; + else if (maxburst >= 4) + psize = STEDMA40_PSIZE_PHY_4; + else + psize = STEDMA40_PSIZE_PHY_1; + } + + info->data_width = addr_width; + info->psize = psize; + info->flow_ctrl = STEDMA40_NO_FLOW_CTRL; + + return 0; +} + /* Runtime reconfiguration extension */ -static void d40_set_runtime_config(struct dma_chan *chan, - struct dma_slave_config *config) +static int d40_set_runtime_config(struct dma_chan *chan, + struct dma_slave_config *config) { struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); struct stedma40_chan_cfg *cfg = &d40c->dma_cfg; - enum dma_slave_buswidth config_addr_width; + enum dma_slave_buswidth src_addr_width, dst_addr_width; dma_addr_t config_addr; - u32 config_maxburst; - enum stedma40_periph_data_width addr_width; - int psize; + u32 src_maxburst, dst_maxburst; + int ret; + + src_addr_width = config->src_addr_width; + src_maxburst = config->src_maxburst; + dst_addr_width = config->dst_addr_width; + dst_maxburst = config->dst_maxburst; if (config->direction == DMA_FROM_DEVICE) { dma_addr_t dev_addr_rx = @@ -2187,8 +2288,11 @@ static void d40_set_runtime_config(struct dma_chan *chan, cfg->dir); cfg->dir = STEDMA40_PERIPH_TO_MEM; - config_addr_width = config->src_addr_width; - config_maxburst = config->src_maxburst; + /* Configure the memory side */ + if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + dst_addr_width = src_addr_width; + if (dst_maxburst == 0) + dst_maxburst = src_maxburst; } else if (config->direction == DMA_TO_DEVICE) { dma_addr_t dev_addr_tx = @@ -2207,68 +2311,39 @@ static void d40_set_runtime_config(struct dma_chan *chan, cfg->dir); cfg->dir = STEDMA40_MEM_TO_PERIPH; - config_addr_width = config->dst_addr_width; - config_maxburst = config->dst_maxburst; - + /* Configure the memory side */ + if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + src_addr_width = dst_addr_width; + if (src_maxburst == 0) + src_maxburst = dst_maxburst; } else { dev_err(d40c->base->dev, "unrecognized channel direction %d\n", config->direction); - return; + return -EINVAL; } - switch (config_addr_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - addr_width = STEDMA40_BYTE_WIDTH; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - addr_width = STEDMA40_HALFWORD_WIDTH; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - addr_width = STEDMA40_WORD_WIDTH; - break; - case DMA_SLAVE_BUSWIDTH_8_BYTES: - addr_width = STEDMA40_DOUBLEWORD_WIDTH; - break; - default: + if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) { dev_err(d40c->base->dev, - "illegal peripheral address width " - "requested (%d)\n", - config->src_addr_width); - return; + "src/dst width/maxburst mismatch: %d*%d != %d*%d\n", + src_maxburst, + src_addr_width, + dst_maxburst, + dst_addr_width); + return -EINVAL; } - if (chan_is_logical(d40c)) { - if (config_maxburst >= 16) - psize = STEDMA40_PSIZE_LOG_16; - else if (config_maxburst >= 8) - psize = STEDMA40_PSIZE_LOG_8; - else if (config_maxburst >= 4) - psize = STEDMA40_PSIZE_LOG_4; - else - psize = STEDMA40_PSIZE_LOG_1; - } else { - if (config_maxburst >= 16) - psize = STEDMA40_PSIZE_PHY_16; - else if (config_maxburst >= 8) - psize = STEDMA40_PSIZE_PHY_8; - else if (config_maxburst >= 4) - psize = STEDMA40_PSIZE_PHY_4; - else if (config_maxburst >= 2) - psize = STEDMA40_PSIZE_PHY_2; - else - psize = STEDMA40_PSIZE_PHY_1; - } + ret = dma40_config_to_halfchannel(d40c, &cfg->src_info, + src_addr_width, + src_maxburst); + if (ret) + return ret; - /* Set up all the endpoint configs */ - cfg->src_info.data_width = addr_width; - cfg->src_info.psize = psize; - cfg->src_info.big_endian = false; - cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL; - cfg->dst_info.data_width = addr_width; - cfg->dst_info.psize = psize; - cfg->dst_info.big_endian = false; - cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL; + ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info, + dst_addr_width, + dst_maxburst); + if (ret) + return ret; /* Fill in register values */ if (chan_is_logical(d40c)) @@ -2281,12 +2356,14 @@ static void d40_set_runtime_config(struct dma_chan *chan, d40c->runtime_addr = config_addr; d40c->runtime_direction = config->direction; dev_dbg(d40c->base->dev, - "configured channel %s for %s, data width %d, " - "maxburst %d bytes, LE, no flow control\n", + "configured channel %s for %s, data width %d/%d, " + "maxburst %d/%d elements, LE, no flow control\n", dma_chan_name(chan), (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX", - config_addr_width, - config_maxburst); + src_addr_width, dst_addr_width, + src_maxburst, dst_maxburst); + + return 0; } static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, @@ -2307,9 +2384,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_RESUME: return d40_resume(d40c); case DMA_SLAVE_CONFIG: - d40_set_runtime_config(chan, + return d40_set_runtime_config(chan, (struct dma_slave_config *) arg); - return 0; default: break; } @@ -2340,7 +2416,9 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, INIT_LIST_HEAD(&d40c->active); INIT_LIST_HEAD(&d40c->queue); + INIT_LIST_HEAD(&d40c->pending_queue); INIT_LIST_HEAD(&d40c->client); + INIT_LIST_HEAD(&d40c->prepare_queue); tasklet_init(&d40c->tasklet, dma_tasklet, (unsigned long) d40c); @@ -2501,25 +2579,6 @@ static int __init d40_phy_res_init(struct d40_base *base) static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) { - static const struct d40_reg_val dma_id_regs[] = { - /* Peripheral Id */ - { .reg = D40_DREG_PERIPHID0, .val = 0x0040}, - { .reg = D40_DREG_PERIPHID1, .val = 0x0000}, - /* - * D40_DREG_PERIPHID2 Depends on HW revision: - * DB8500ed has 0x0008, - * ? has 0x0018, - * DB8500v1 has 0x0028 - * DB8500v2 has 0x0038 - */ - { .reg = D40_DREG_PERIPHID3, .val = 0x0000}, - - /* PCell Id */ - { .reg = D40_DREG_CELLID0, .val = 0x000d}, - { .reg = D40_DREG_CELLID1, .val = 0x00f0}, - { .reg = D40_DREG_CELLID2, .val = 0x0005}, - { .reg = D40_DREG_CELLID3, .val = 0x00b1} - }; struct stedma40_platform_data *plat_data; struct clk *clk = NULL; void __iomem *virtbase = NULL; @@ -2528,8 +2587,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) int num_log_chans = 0; int num_phy_chans; int i; - u32 val; - u32 rev; + u32 pid; + u32 cid; + u8 rev; clk = clk_get(&pdev->dev, NULL); @@ -2553,32 +2613,32 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) if (!virtbase) goto failure; - /* HW version check */ - for (i = 0; i < ARRAY_SIZE(dma_id_regs); i++) { - if (dma_id_regs[i].val != - readl(virtbase + dma_id_regs[i].reg)) { - d40_err(&pdev->dev, - "Unknown hardware! Expected 0x%x at 0x%x but got 0x%x\n", - dma_id_regs[i].val, - dma_id_regs[i].reg, - readl(virtbase + dma_id_regs[i].reg)); - goto failure; - } - } - - /* Get silicon revision and designer */ - val = readl(virtbase + D40_DREG_PERIPHID2); + /* This is just a regular AMBA PrimeCell ID actually */ + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i) + & 255) << (i * 8); + for (cid = 0, i = 0; i < 4; i++) + cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i) + & 255) << (i * 8); - if ((val & D40_DREG_PERIPHID2_DESIGNER_MASK) != - D40_HW_DESIGNER) { + if (cid != AMBA_CID) { + d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n"); + goto failure; + } + if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) { d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n", - val & D40_DREG_PERIPHID2_DESIGNER_MASK, - D40_HW_DESIGNER); + AMBA_MANF_BITS(pid), + AMBA_VENDOR_ST); goto failure; } - - rev = (val & D40_DREG_PERIPHID2_REV_MASK) >> - D40_DREG_PERIPHID2_REV_POS; + /* + * HW revision: + * DB8500ed has revision 0 + * ? has revision 1 + * DB8500v1 has revision 2 + * DB8500v2 has revision 3 + */ + rev = AMBA_REV_BITS(pid); /* The number of physical channels on this HW */ num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index 195ee65..b44c4551 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h @@ -184,9 +184,6 @@ #define D40_DREG_PERIPHID0 0xFE0 #define D40_DREG_PERIPHID1 0xFE4 #define D40_DREG_PERIPHID2 0xFE8 -#define D40_DREG_PERIPHID2_REV_POS 4 -#define D40_DREG_PERIPHID2_REV_MASK (0xf << D40_DREG_PERIPHID2_REV_POS) -#define D40_DREG_PERIPHID2_DESIGNER_MASK 0xf #define D40_DREG_PERIPHID3 0xFEC #define D40_DREG_CELLID0 0xFF0 #define D40_DREG_CELLID1 0xFF4 diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index f69f90a..a4a398f 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -753,7 +753,7 @@ static int __devinit td_probe(struct platform_device *pdev) INIT_LIST_HEAD(&td->dma.channels); - for (i = 0; i < pdata->nr_channels; i++, td->dma.chancnt++) { + for (i = 0; i < pdata->nr_channels; i++) { struct timb_dma_chan *td_chan = &td->channels[i]; struct timb_dma_platform_data_channel *pchan = pdata->channels + i; @@ -762,12 +762,11 @@ static int __devinit td_probe(struct platform_device *pdev) if ((i % 2) == pchan->rx) { dev_err(&pdev->dev, "Wrong channel configuration\n"); err = -EINVAL; - goto err_tasklet_kill; + goto err_free_irq; } td_chan->chan.device = &td->dma; td_chan->chan.cookie = 1; - td_chan->chan.chan_id = i; spin_lock_init(&td_chan->lock); INIT_LIST_HEAD(&td_chan->active_list); INIT_LIST_HEAD(&td_chan->queue); -- cgit v1.1