summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/Kconfig11
-rw-r--r--drivers/dma/Makefile8
-rw-r--r--drivers/dma/amba-pl08x.c1217
-rw-r--r--drivers/dma/at_hdmac.c21
-rw-r--r--drivers/dma/coh901318.c4
-rw-r--r--drivers/dma/fsldma.c24
-rw-r--r--drivers/dma/fsldma.h9
-rw-r--r--drivers/dma/imx-dma.c28
-rw-r--r--drivers/dma/imx-sdma.c264
-rw-r--r--drivers/dma/intel_mid_dma.c47
-rw-r--r--drivers/dma/ioat/Makefile2
-rw-r--r--drivers/dma/iop-adma.c4
-rw-r--r--drivers/dma/ipu/ipu_idmac.c50
-rw-r--r--drivers/dma/mpc512x_dma.c192
-rw-r--r--drivers/dma/mv_xor.c2
-rw-r--r--drivers/dma/pch_dma.c34
-rw-r--r--drivers/dma/pl330.c2
-rw-r--r--drivers/dma/ppc4xx/adma.c16
-rw-r--r--drivers/dma/shdma.c132
-rw-r--r--drivers/dma/shdma.h1
-rw-r--r--drivers/dma/ste_dma40.c191
-rw-r--r--drivers/dma/ste_dma40_ll.c246
-rw-r--r--drivers/dma/ste_dma40_ll.h36
-rw-r--r--drivers/dma/timb_dma.c2
24 files changed, 1399 insertions, 1144 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 6ee23592700a..1c28816152fa 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -109,7 +109,7 @@ config FSL_DMA
config MPC512X_DMA
tristate "Freescale MPC512x built-in DMA engine support"
- depends on PPC_MPC512x
+ depends on PPC_MPC512x || PPC_MPC831x
select DMA_ENGINE
---help---
Enable support for the Freescale MPC512x built-in DMA engine.
@@ -200,11 +200,16 @@ config PL330_DMA
platform_data for a dma-pl330 device.
config PCH_DMA
- tristate "Topcliff (Intel EG20T) PCH DMA support"
+ tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7213 IOH DMA support"
depends on PCI && X86
select DMA_ENGINE
help
- Enable support for the Topcliff (Intel EG20T) PCH DMA engine.
+ Enable support for Intel EG20T PCH DMA engine.
+
+ This driver also can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/
+ Output Hub) which is for IVI(In-Vehicle Infotainment) use.
+ ML7213 is companion chip for Intel Atom E6xx series.
+ ML7213 is completely compatible for Intel EG20T PCH.
config IMX_SDMA
tristate "i.MX SDMA support"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index a8a84f4587f2..1be065a62f8c 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -1,9 +1,5 @@
-ifeq ($(CONFIG_DMADEVICES_DEBUG),y)
- EXTRA_CFLAGS += -DDEBUG
-endif
-ifeq ($(CONFIG_DMADEVICES_VDEBUG),y)
- EXTRA_CFLAGS += -DVERBOSE_DEBUG
-endif
+ccflags-$(CONFIG_DMADEVICES_DEBUG) := -DDEBUG
+ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
obj-$(CONFIG_NET_DMA) += iovlock.o
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index b605cc9ac3a2..e6d7228b1479 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -19,14 +19,14 @@
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
- * The full GNU General Public License is iin this distribution in the
- * file called COPYING.
+ * The full GNU General Public License is in this distribution in the file
+ * called COPYING.
*
* Documentation: ARM DDI 0196G == PL080
- * Documentation: ARM DDI 0218E == PL081
+ * Documentation: ARM DDI 0218E == PL081
*
- * PL080 & PL081 both have 16 sets of DMA signals that can be routed to
- * any channel.
+ * PL080 & PL081 both have 16 sets of DMA signals that can be routed to any
+ * channel.
*
* The PL080 has 8 channels available for simultaneous use, and the PL081
* has only two channels. So on these DMA controllers the number of channels
@@ -53,7 +53,23 @@
*
* ASSUMES default (little) endianness for DMA transfers
*
- * Only DMAC flow control is implemented
+ * The PL08x has two flow control settings:
+ * - DMAC flow control: the transfer size defines the number of transfers
+ * which occur for the current LLI entry, and the DMAC raises TC at the
+ * end of every LLI entry. Observed behaviour shows the DMAC listening
+ * to both the BREQ and SREQ signals (contrary to documented),
+ * transferring data if either is active. The LBREQ and LSREQ signals
+ * are ignored.
+ *
+ * - Peripheral flow control: the transfer size is ignored (and should be
+ * zero). The data is transferred from the current LLI entry, until
+ * 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
@@ -61,50 +77,40 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/dmapool.h>
-#include <linux/amba/bus.h>
#include <linux/dmaengine.h>
+#include <linux/amba/bus.h>
#include <linux/amba/pl08x.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/hardware/pl080.h>
-#include <asm/dma.h>
-#include <asm/mach/dma.h>
-#include <asm/atomic.h>
-#include <asm/processor.h>
-#include <asm/cacheflush.h>
#define DRIVER_NAME "pl08xdmac"
/**
- * struct vendor_data - vendor-specific config parameters
- * for PL08x derivates
- * @name: the name of this specific variant
+ * struct vendor_data - vendor-specific config parameters for PL08x derivatives
* @channels: the number of channels available in this variant
- * @dualmaster: whether this version supports dual AHB masters
- * or not.
+ * @dualmaster: whether this version supports dual AHB masters or not.
*/
struct vendor_data {
- char *name;
u8 channels;
bool dualmaster;
};
/*
* PL08X private data structures
- * An LLI struct - see pl08x TRM
- * Note that next uses bit[0] as a bus bit,
- * start & end do not - their bus bit info
- * is in cctl
+ * An LLI struct - see PL08x TRM. Note that next uses bit[0] as a bus bit,
+ * start & end do not - their bus bit info is in cctl. Also note that these
+ * are fixed 32-bit quantities.
*/
-struct lli {
- dma_addr_t src;
- dma_addr_t dst;
- dma_addr_t next;
+struct pl08x_lli {
+ u32 src;
+ u32 dst;
+ u32 lli;
u32 cctl;
};
@@ -119,6 +125,8 @@ struct 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
+ * @mem_buses: set to indicate memory transfers on AHB2.
* @lock: a spinlock for this struct
*/
struct pl08x_driver_data {
@@ -126,11 +134,13 @@ struct pl08x_driver_data {
struct dma_device memcpy;
void __iomem *base;
struct amba_device *adev;
- struct vendor_data *vd;
+ const struct vendor_data *vd;
struct pl08x_platform_data *pd;
struct pl08x_phy_chan *phy_chans;
struct dma_pool *pool;
int pool_ctr;
+ u8 lli_buses;
+ u8 mem_buses;
spinlock_t lock;
};
@@ -152,9 +162,9 @@ struct pl08x_driver_data {
/* Size (bytes) of each LLI buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000
-/* Maximimum times we call dma_pool_alloc on this pool without freeing */
+/* 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 lli))
+#define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli))
#define PL08X_ALIGN 8
static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
@@ -162,6 +172,11 @@ static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
return container_of(chan, struct pl08x_dma_chan, chan);
}
+static inline struct pl08x_txd *to_pl08x_txd(struct dma_async_tx_descriptor *tx)
+{
+ return container_of(tx, struct pl08x_txd, tx);
+}
+
/*
* Physical channel handling
*/
@@ -177,103 +192,63 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
/*
* Set the initial DMA register values i.e. those for the first LLI
- * The next lli pointer and the configuration interrupt bit have
- * been set when the LLIs were constructed
+ * The next LLI pointer and the configuration interrupt bit have
+ * been set when the LLIs were constructed. Poke them into the hardware
+ * and start the transfer.
*/
-static void pl08x_set_cregs(struct pl08x_driver_data *pl08x,
- struct pl08x_phy_chan *ch)
+static void pl08x_start_txd(struct pl08x_dma_chan *plchan,
+ struct pl08x_txd *txd)
{
- /* Wait for channel inactive */
- while (pl08x_phy_channel_busy(ch))
- ;
-
- dev_vdbg(&pl08x->adev->dev,
- "WRITE channel %d: csrc=%08x, cdst=%08x, "
- "cctl=%08x, clli=%08x, ccfg=%08x\n",
- ch->id,
- ch->csrc,
- ch->cdst,
- ch->cctl,
- ch->clli,
- ch->ccfg);
-
- writel(ch->csrc, ch->base + PL080_CH_SRC_ADDR);
- writel(ch->cdst, ch->base + PL080_CH_DST_ADDR);
- writel(ch->clli, ch->base + PL080_CH_LLI);
- writel(ch->cctl, ch->base + PL080_CH_CONTROL);
- writel(ch->ccfg, ch->base + PL080_CH_CONFIG);
-}
-
-static inline void pl08x_config_phychan_for_txd(struct pl08x_dma_chan *plchan)
-{
- struct pl08x_channel_data *cd = plchan->cd;
+ struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_phy_chan *phychan = plchan->phychan;
- struct pl08x_txd *txd = plchan->at;
-
- /* Copy the basic control register calculated at transfer config */
- phychan->csrc = txd->csrc;
- phychan->cdst = txd->cdst;
- phychan->clli = txd->clli;
- phychan->cctl = txd->cctl;
-
- /* Assign the signal to the proper control registers */
- phychan->ccfg = cd->ccfg;
- phychan->ccfg &= ~PL080_CONFIG_SRC_SEL_MASK;
- phychan->ccfg &= ~PL080_CONFIG_DST_SEL_MASK;
- /* If it wasn't set from AMBA, ignore it */
- if (txd->direction == DMA_TO_DEVICE)
- /* Select signal as destination */
- phychan->ccfg |=
- (phychan->signal << PL080_CONFIG_DST_SEL_SHIFT);
- else if (txd->direction == DMA_FROM_DEVICE)
- /* Select signal as source */
- phychan->ccfg |=
- (phychan->signal << PL080_CONFIG_SRC_SEL_SHIFT);
- /* Always enable error interrupts */
- phychan->ccfg |= PL080_CONFIG_ERR_IRQ_MASK;
- /* Always enable terminal interrupts */
- phychan->ccfg |= PL080_CONFIG_TC_IRQ_MASK;
-}
-
-/*
- * Enable the DMA channel
- * Assumes all other configuration bits have been set
- * as desired before this code is called
- */
-static void pl08x_enable_phy_chan(struct pl08x_driver_data *pl08x,
- struct pl08x_phy_chan *ch)
-{
+ struct pl08x_lli *lli = &txd->llis_va[0];
u32 val;
- /*
- * Do not access config register until channel shows as disabled
- */
- while (readl(pl08x->base + PL080_EN_CHAN) & (1 << ch->id))
- ;
+ plchan->at = txd;
- /*
- * Do not access config register until channel shows as inactive
- */
- val = readl(ch->base + PL080_CH_CONFIG);
+ /* Wait for channel inactive */
+ while (pl08x_phy_channel_busy(phychan))
+ cpu_relax();
+
+ dev_vdbg(&pl08x->adev->dev,
+ "WRITE channel %d: csrc=0x%08x, cdst=0x%08x, "
+ "clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n",
+ phychan->id, lli->src, lli->dst, lli->lli, lli->cctl,
+ txd->ccfg);
+
+ writel(lli->src, phychan->base + PL080_CH_SRC_ADDR);
+ writel(lli->dst, phychan->base + PL080_CH_DST_ADDR);
+ writel(lli->lli, phychan->base + PL080_CH_LLI);
+ writel(lli->cctl, phychan->base + PL080_CH_CONTROL);
+ writel(txd->ccfg, phychan->base + PL080_CH_CONFIG);
+
+ /* Enable the DMA channel */
+ /* Do not access config register until channel shows as disabled */
+ while (readl(pl08x->base + PL080_EN_CHAN) & (1 << phychan->id))
+ cpu_relax();
+
+ /* Do not access config register until channel shows as inactive */
+ val = readl(phychan->base + PL080_CH_CONFIG);
while ((val & PL080_CONFIG_ACTIVE) || (val & PL080_CONFIG_ENABLE))
- val = readl(ch->base + PL080_CH_CONFIG);
+ val = readl(phychan->base + PL080_CH_CONFIG);
- writel(val | PL080_CONFIG_ENABLE, ch->base + PL080_CH_CONFIG);
+ writel(val | PL080_CONFIG_ENABLE, phychan->base + PL080_CH_CONFIG);
}
/*
- * Overall DMAC remains enabled always.
- *
- * Disabling individual channels could lose data.
+ * Pause the channel by setting the HALT bit.
*
- * Disable the peripheral DMA after disabling the DMAC
- * in order to allow the DMAC FIFO to drain, and
- * hence allow the channel to show inactive
+ * For M->P transfers, pause the DMAC first and then stop the peripheral -
+ * the FIFO can only drain if the peripheral is still requesting data.
+ * (note: this can still timeout if the DMAC FIFO never drains of data.)
*
+ * For P->M transfers, disable the peripheral first to stop it filling
+ * the DMAC FIFO, and then pause the DMAC.
*/
static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
{
u32 val;
+ int timeout;
/* Set the HALT bit and wait for the FIFO to drain */
val = readl(ch->base + PL080_CH_CONFIG);
@@ -281,8 +256,13 @@ static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
writel(val, ch->base + PL080_CH_CONFIG);
/* Wait for channel inactive */
- while (pl08x_phy_channel_busy(ch))
- ;
+ for (timeout = 1000; timeout; timeout--) {
+ if (!pl08x_phy_channel_busy(ch))
+ break;
+ udelay(1);
+ }
+ if (pl08x_phy_channel_busy(ch))
+ pr_err("pl08x: channel%u timeout waiting for pause\n", ch->id);
}
static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
@@ -296,19 +276,24 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
}
-/* Stops the channel */
-static void pl08x_stop_phy_chan(struct pl08x_phy_chan *ch)
+/*
+ * pl08x_terminate_phy_chan() stops the channel, clears the FIFO and
+ * clears any pending interrupt status. This should not be used for
+ * an on-going transfer, but as a method of shutting down a channel
+ * (eg, when it's no longer used) or terminating a transfer.
+ */
+static void pl08x_terminate_phy_chan(struct pl08x_driver_data *pl08x,
+ struct pl08x_phy_chan *ch)
{
- u32 val;
+ u32 val = readl(ch->base + PL080_CH_CONFIG);
- pl08x_pause_phy_chan(ch);
+ val &= ~(PL080_CONFIG_ENABLE | PL080_CONFIG_ERR_IRQ_MASK |
+ PL080_CONFIG_TC_IRQ_MASK);
- /* Disable channel */
- val = readl(ch->base + PL080_CH_CONFIG);
- val &= ~PL080_CONFIG_ENABLE;
- val &= ~PL080_CONFIG_ERR_IRQ_MASK;
- val &= ~PL080_CONFIG_TC_IRQ_MASK;
writel(val, ch->base + PL080_CH_CONFIG);
+
+ writel(1 << ch->id, pl08x->base + PL080_ERR_CLEAR);
+ writel(1 << ch->id, pl08x->base + PL080_TC_CLEAR);
}
static inline u32 get_bytes_in_cctl(u32 cctl)
@@ -333,54 +318,56 @@ static inline u32 get_bytes_in_cctl(u32 cctl)
static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
{
struct pl08x_phy_chan *ch;
- struct pl08x_txd *txdi = NULL;
struct pl08x_txd *txd;
unsigned long flags;
- u32 bytes = 0;
+ size_t bytes = 0;
spin_lock_irqsave(&plchan->lock, flags);
-
ch = plchan->phychan;
txd = plchan->at;
/*
- * Next follow the LLIs to get the number of pending bytes in the
- * currently active transaction.
+ * Follow the LLIs to get the number of remaining
+ * bytes in the currently active transaction.
*/
if (ch && txd) {
- struct lli *llis_va = txd->llis_va;
- struct lli *llis_bus = (struct lli *) txd->llis_bus;
- u32 clli = readl(ch->base + PL080_CH_LLI);
+ u32 clli = readl(ch->base + PL080_CH_LLI) & ~PL080_LLI_LM_AHB2;
- /* First get the bytes in the current active LLI */
+ /* First get the remaining bytes in the active transfer */
bytes = get_bytes_in_cctl(readl(ch->base + PL080_CH_CONTROL));
if (clli) {
- int i = 0;
+ struct pl08x_lli *llis_va = txd->llis_va;
+ dma_addr_t llis_bus = txd->llis_bus;
+ int index;
+
+ BUG_ON(clli < llis_bus || clli >= llis_bus +
+ sizeof(struct pl08x_lli) * MAX_NUM_TSFR_LLIS);
+
+ /*
+ * Locate the next LLI - as this is an array,
+ * it's simple maths to find.
+ */
+ index = (clli - llis_bus) / sizeof(struct pl08x_lli);
- /* Forward to the LLI pointed to by clli */
- while ((clli != (u32) &(llis_bus[i])) &&
- (i < MAX_NUM_TSFR_LLIS))
- i++;
+ for (; index < MAX_NUM_TSFR_LLIS; index++) {
+ bytes += get_bytes_in_cctl(llis_va[index].cctl);
- while (clli) {
- bytes += get_bytes_in_cctl(llis_va[i].cctl);
/*
- * A clli of 0x00000000 will terminate the
- * LLI list
+ * A LLI pointer of 0 terminates the LLI list
*/
- clli = llis_va[i].next;
- i++;
+ if (!llis_va[index].lli)
+ break;
}
}
}
/* Sum up all queued transactions */
- if (!list_empty(&plchan->desc_list)) {
- list_for_each_entry(txdi, &plchan->desc_list, node) {
+ if (!list_empty(&plchan->pend_list)) {
+ struct pl08x_txd *txdi;
+ list_for_each_entry(txdi, &plchan->pend_list, node) {
bytes += txdi->len;
}
-
}
spin_unlock_irqrestore(&plchan->lock, flags);
@@ -390,6 +377,10 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
/*
* Allocate a physical channel for a virtual channel
+ *
+ * Try to locate a physical channel to be used for this transfer. If all
+ * are taken return NULL and the requester will have to cope by using
+ * some fallback PIO mode or retrying later.
*/
static struct pl08x_phy_chan *
pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
@@ -399,12 +390,6 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
unsigned long flags;
int i;
- /*
- * Try to locate a physical channel to be used for
- * this transfer. If all are taken return NULL and
- * the requester will have to cope by using some fallback
- * PIO mode or retrying later.
- */
for (i = 0; i < pl08x->vd->channels; i++) {
ch = &pl08x->phy_chans[i];
@@ -433,13 +418,12 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
{
unsigned long flags;
+ spin_lock_irqsave(&ch->lock, flags);
+
/* Stop the channel and clear its interrupts */
- pl08x_stop_phy_chan(ch);
- writel((1 << ch->id), pl08x->base + PL080_ERR_CLEAR);
- writel((1 << ch->id), pl08x->base + PL080_TC_CLEAR);
+ pl08x_terminate_phy_chan(pl08x, ch);
/* Mark it as free */
- spin_lock_irqsave(&ch->lock, flags);
ch->serving = NULL;
spin_unlock_irqrestore(&ch->lock, flags);
}
@@ -465,11 +449,11 @@ static inline unsigned int pl08x_get_bytes_for_cctl(unsigned int coded)
}
static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
- u32 tsize)
+ size_t tsize)
{
u32 retbits = cctl;
- /* Remove all src, dst and transfersize bits */
+ /* Remove all src, dst and transfer size bits */
retbits &= ~PL080_CONTROL_DWIDTH_MASK;
retbits &= ~PL080_CONTROL_SWIDTH_MASK;
retbits &= ~PL080_CONTROL_TRANSFER_SIZE_MASK;
@@ -509,95 +493,87 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
return retbits;
}
+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;
+};
+
/*
- * 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 this prefers the
+ * destination bus if both available if fixed address on one bus the
+ * other will be chosen
*/
-void pl08x_choose_master_bus(struct pl08x_bus_data *src_bus,
- struct pl08x_bus_data *dst_bus, struct pl08x_bus_data **mbus,
- struct pl08x_bus_data **sbus, u32 cctl)
+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 = src_bus;
- *sbus = dst_bus;
+ *mbus = &bd->srcbus;
+ *sbus = &bd->dstbus;
} else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
- *mbus = dst_bus;
- *sbus = src_bus;
+ *mbus = &bd->dstbus;
+ *sbus = &bd->srcbus;
} else {
- if (dst_bus->buswidth == 4) {
- *mbus = dst_bus;
- *sbus = src_bus;
- } else if (src_bus->buswidth == 4) {
- *mbus = src_bus;
- *sbus = dst_bus;
- } else if (dst_bus->buswidth == 2) {
- *mbus = dst_bus;
- *sbus = src_bus;
- } else if (src_bus->buswidth == 2) {
- *mbus = src_bus;
- *sbus = dst_bus;
+ 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) {
+ *mbus = &bd->dstbus;
+ *sbus = &bd->srcbus;
+ } else if (bd->srcbus.buswidth == 2) {
+ *mbus = &bd->srcbus;
+ *sbus = &bd->dstbus;
} else {
- /* src_bus->buswidth == 1 */
- *mbus = dst_bus;
- *sbus = src_bus;
+ /* bd->srcbus.buswidth == 1 */
+ *mbus = &bd->dstbus;
+ *sbus = &bd->srcbus;
}
}
}
/*
- * Fills in one LLI for a certain transfer descriptor
- * and advance the counter
+ * Fills in one LLI for a certain transfer descriptor and advance the counter
*/
-int pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
- struct pl08x_txd *txd, int num_llis, int len,
- u32 cctl, u32 *remainder)
+static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
+ int num_llis, int len, u32 cctl)
{
- struct lli *llis_va = txd->llis_va;
- struct lli *llis_bus = (struct lli *) txd->llis_bus;
+ struct pl08x_lli *llis_va = bd->txd->llis_va;
+ dma_addr_t llis_bus = bd->txd->llis_bus;
BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);
- llis_va[num_llis].cctl = cctl;
- llis_va[num_llis].src = txd->srcbus.addr;
- llis_va[num_llis].dst = txd->dstbus.addr;
-
- /*
- * On versions with dual masters, you can optionally AND on
- * PL080_LLI_LM_AHB2 to the LLI to tell the hardware to read
- * in new LLIs with that controller, but we always try to
- * choose AHB1 to point into memory. The idea is to have AHB2
- * fixed on the peripheral and AHB1 messing around in the
- * memory. So we don't manipulate this bit currently.
- */
-
- llis_va[num_llis].next =
- (dma_addr_t)((u32) &(llis_bus[num_llis + 1]));
+ 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;
if (cctl & PL080_CONTROL_SRC_INCR)
- txd->srcbus.addr += len;
+ bd->srcbus.addr += len;
if (cctl & PL080_CONTROL_DST_INCR)
- txd->dstbus.addr += len;
+ bd->dstbus.addr += len;
- *remainder -= len;
+ BUG_ON(bd->remainder < len);
- return num_llis + 1;
+ bd->remainder -= len;
}
/*
- * Return number of bytes to fill to boundary, or len
+ * Return number of bytes to fill to boundary, or len.
+ * This calculation works for any value of addr.
*/
-static inline u32 pl08x_pre_boundary(u32 addr, u32 len)
+static inline size_t pl08x_pre_boundary(u32 addr, size_t len)
{
- u32 boundary;
+ size_t boundary_len = PL08X_BOUNDARY_SIZE -
+ (addr & (PL08X_BOUNDARY_SIZE - 1));
- boundary = ((addr >> PL08X_BOUNDARY_SHIFT) + 1)
- << PL08X_BOUNDARY_SHIFT;
-
- if (boundary < addr + len)
- return boundary - addr;
- else
- return len;
+ return min(boundary_len, len);
}
/*
@@ -608,20 +584,13 @@ static inline u32 pl08x_pre_boundary(u32 addr, u32 len)
static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
struct pl08x_txd *txd)
{
- struct pl08x_channel_data *cd = txd->cd;
struct pl08x_bus_data *mbus, *sbus;
- u32 remainder;
+ struct pl08x_lli_build_data bd;
int num_llis = 0;
u32 cctl;
- int max_bytes_per_lli;
- int total_bytes = 0;
- struct lli *llis_va;
- struct lli *llis_bus;
-
- if (!txd) {
- dev_err(&pl08x->adev->dev, "%s no descriptor\n", __func__);
- return 0;
- }
+ size_t max_bytes_per_lli;
+ size_t total_bytes = 0;
+ struct pl08x_lli *llis_va;
txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT,
&txd->llis_bus);
@@ -632,121 +601,79 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
pl08x->pool_ctr++;
- /*
- * Initialize bus values for this transfer
- * from the passed optimal values
- */
- if (!cd) {
- dev_err(&pl08x->adev->dev, "%s no channel data\n", __func__);
- return 0;
- }
-
- /* Get the default CCTL from the platform data */
- cctl = cd->cctl;
+ /* Get the default CCTL */
+ cctl = txd->cctl;
- /*
- * On the PL080 we have two bus masters and we
- * should select one for source and one for
- * destination. We try to use AHB2 for the
- * bus which does not increment (typically the
- * peripheral) else we just choose something.
- */
- cctl &= ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
- if (pl08x->vd->dualmaster) {
- if (cctl & PL080_CONTROL_SRC_INCR)
- /* Source increments, use AHB2 for destination */
- cctl |= PL080_CONTROL_DST_AHB2;
- else if (cctl & PL080_CONTROL_DST_INCR)
- /* Destination increments, use AHB2 for source */
- cctl |= PL080_CONTROL_SRC_AHB2;
- else
- /* Just pick something, source AHB1 dest AHB2 */
- cctl |= PL080_CONTROL_DST_AHB2;
- }
+ bd.txd = txd;
+ bd.pl08x = pl08x;
+ bd.srcbus.addr = txd->src_addr;
+ bd.dstbus.addr = txd->dst_addr;
/* Find maximum width of the source bus */
- txd->srcbus.maxwidth =
+ bd.srcbus.maxwidth =
pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_SWIDTH_MASK) >>
PL080_CONTROL_SWIDTH_SHIFT);
/* Find maximum width of the destination bus */
- txd->dstbus.maxwidth =
+ bd.dstbus.maxwidth =
pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_DWIDTH_MASK) >>
PL080_CONTROL_DWIDTH_SHIFT);
/* Set up the bus widths to the maximum */
- txd->srcbus.buswidth = txd->srcbus.maxwidth;
- txd->dstbus.buswidth = txd->dstbus.maxwidth;
+ 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__, txd->srcbus.buswidth, txd->dstbus.buswidth);
+ __func__, bd.srcbus.buswidth, bd.dstbus.buswidth);
/*
* Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
*/
- max_bytes_per_lli = min(txd->srcbus.buswidth, txd->dstbus.buswidth) *
+ 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 = %d\n",
+ "%s max bytes per lli = %zu\n",
__func__, max_bytes_per_lli);
/* We need to count this down to zero */
- remainder = txd->len;
+ bd.remainder = txd->len;
dev_vdbg(&pl08x->adev->dev,
- "%s remainder = %d\n",
- __func__, remainder);
+ "%s remainder = %zu\n",
+ __func__, bd.remainder);
/*
* Choose bus to align to
* - prefers destination bus if both available
* - if fixed address on one bus chooses other
- * - modifies cctl to choose an apropriate master
- */
- pl08x_choose_master_bus(&txd->srcbus, &txd->dstbus,
- &mbus, &sbus, cctl);
-
-
- /*
- * The lowest bit of the LLI register
- * is also used to indicate which master to
- * use for reading the LLIs.
*/
+ pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
if (txd->len < mbus->buswidth) {
- /*
- * Less than a bus width available
- * - send as single bytes
- */
- while (remainder) {
+ /* 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 %08x)\n",
- __func__, remainder);
+ "less than a bus width (remain 0x%08x)\n",
+ __func__, bd.remainder);
cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- num_llis =
- pl08x_fill_lli_for_desc(pl08x, txd, num_llis, 1,
- cctl, &remainder);
+ pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
total_bytes++;
}
} else {
- /*
- * Make one byte LLIs until master bus is aligned
- * - slave will then be aligned also
- */
+ /* 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 %08x)\n",
- __func__, remainder);
+ "(remain 0x%08x)\n",
+ __func__, bd.remainder);
cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
- num_llis = pl08x_fill_lli_for_desc
- (pl08x, txd, num_llis, 1, cctl, &remainder);
+ pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
total_bytes++;
}
/*
- * Master now aligned
+ * Master now aligned
* - if slave is not then we must set its width down
*/
if (sbus->addr % sbus->buswidth) {
@@ -761,63 +688,51 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
* Make largest possible LLIs until less than one bus
* width left
*/
- while (remainder > (mbus->buswidth - 1)) {
- int lli_len, target_len;
- int tsize;
- int odd_bytes;
+ while (bd.remainder > (mbus->buswidth - 1)) {
+ size_t lli_len, target_len, tsize, odd_bytes;
/*
* If enough left try to send max possible,
* otherwise try to send the remainder
*/
- target_len = remainder;
- if (remainder > max_bytes_per_lli)
- target_len = max_bytes_per_lli;
+ target_len = min(bd.remainder, max_bytes_per_lli);
/*
- * Set bus lengths for incrementing busses
- * to number of bytes which fill to next memory
- * boundary
+ * Set bus lengths for incrementing buses to the
+ * number of bytes which fill to next memory boundary,
+ * limiting on the target length calculated above.
*/
if (cctl & PL080_CONTROL_SRC_INCR)
- txd->srcbus.fill_bytes =
- pl08x_pre_boundary(
- txd->srcbus.addr,
- remainder);
+ bd.srcbus.fill_bytes =
+ pl08x_pre_boundary(bd.srcbus.addr,
+ target_len);
else
- txd->srcbus.fill_bytes =
- max_bytes_per_lli;
+ bd.srcbus.fill_bytes = target_len;
if (cctl & PL080_CONTROL_DST_INCR)
- txd->dstbus.fill_bytes =
- pl08x_pre_boundary(
- txd->dstbus.addr,
- remainder);
+ bd.dstbus.fill_bytes =
+ pl08x_pre_boundary(bd.dstbus.addr,
+ target_len);
else
- txd->dstbus.fill_bytes =
- max_bytes_per_lli;
+ bd.dstbus.fill_bytes = target_len;
- /*
- * Find the nearest
- */
- lli_len = min(txd->srcbus.fill_bytes,
- txd->dstbus.fill_bytes);
+ /* Find the nearest */
+ lli_len = min(bd.srcbus.fill_bytes,
+ bd.dstbus.fill_bytes);
- BUG_ON(lli_len > remainder);
+ BUG_ON(lli_len > bd.remainder);
if (lli_len <= 0) {
dev_err(&pl08x->adev->dev,
- "%s lli_len is %d, <= 0\n",
+ "%s lli_len is %zu, <= 0\n",
__func__, lli_len);
return 0;
}
if (lli_len == target_len) {
/*
- * Can send what we wanted
- */
- /*
- * Maintain alignment
+ * Can send what we wanted.
+ * Maintain alignment
*/
lli_len = (lli_len/mbus->buswidth) *
mbus->buswidth;
@@ -825,17 +740,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
} 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
+ * 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.
*/
odd_bytes = lli_len % mbus->buswidth;
- /*
- * - and that we are working in multiples
- * of the bus widths
- */
lli_len -= odd_bytes;
}
@@ -855,41 +767,38 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
if (target_len != lli_len) {
dev_vdbg(&pl08x->adev->dev,
- "%s can't send what we want. Desired %08x, lli of %08x bytes in txd of %08x\n",
+ "%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,
- txd->srcbus.buswidth,
- txd->dstbus.buswidth,
+ bd.srcbus.buswidth,
+ bd.dstbus.buswidth,
tsize);
dev_vdbg(&pl08x->adev->dev,
- "%s fill lli with single lli chunk of size %08x (remainder %08x)\n",
- __func__, lli_len, remainder);
- num_llis = pl08x_fill_lli_for_desc(pl08x, txd,
- num_llis, lli_len, cctl,
- &remainder);
+ "%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)\n",
+ __func__, lli_len, bd.remainder);
+ pl08x_fill_lli_for_desc(&bd, num_llis++,
+ lli_len, cctl);
total_bytes += lli_len;
}
if (odd_bytes) {
/*
- * Creep past the boundary,
- * maintaining master alignment
+ * Creep past the boundary, maintaining
+ * master alignment
*/
int j;
for (j = 0; (j < mbus->buswidth)
- && (remainder); j++) {
+ && (bd.remainder); j++) {
cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
dev_vdbg(&pl08x->adev->dev,
- "%s align with boundardy, single byte (remain %08x)\n",
- __func__, remainder);
- num_llis =
- pl08x_fill_lli_for_desc(pl08x,
- txd, num_llis, 1,
- cctl, &remainder);
+ "%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++;
}
}
@@ -898,25 +807,18 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
/*
* Send any odd bytes
*/
- if (remainder < 0) {
- dev_err(&pl08x->adev->dev, "%s remainder not fitted 0x%08x bytes\n",
- __func__, remainder);
- return 0;
- }
-
- while (remainder) {
+ while (bd.remainder) {
cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
dev_vdbg(&pl08x->adev->dev,
- "%s align with boundardy, single odd byte (remain %d)\n",
- __func__, remainder);
- num_llis = pl08x_fill_lli_for_desc(pl08x, txd, num_llis,
- 1, cctl, &remainder);
+ "%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 != txd->len) {
dev_err(&pl08x->adev->dev,
- "%s size of encoded lli:s don't match total txd, transferred 0x%08x from size 0x%08x\n",
+ "%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;
}
@@ -927,41 +829,12 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
__func__, (u32) MAX_NUM_TSFR_LLIS);
return 0;
}
- /*
- * Decide whether this is a loop or a terminated transfer
- */
- llis_va = txd->llis_va;
- llis_bus = (struct lli *) txd->llis_bus;
-
- if (cd->circular_buffer) {
- /*
- * Loop the circular buffer so that the next element
- * points back to the beginning of the LLI.
- */
- llis_va[num_llis - 1].next =
- (dma_addr_t)((unsigned int)&(llis_bus[0]));
- } else {
- /*
- * On non-circular buffers, the final LLI terminates
- * the LLI.
- */
- llis_va[num_llis - 1].next = 0;
- /*
- * The final LLI element shall also fire an interrupt
- */
- llis_va[num_llis - 1].cctl |= PL080_CONTROL_TC_IRQ_EN;
- }
-
- /* Now store the channel register values */
- txd->csrc = llis_va[0].src;
- txd->cdst = llis_va[0].dst;
- if (num_llis > 1)
- txd->clli = llis_va[0].next;
- else
- txd->clli = 0;
- txd->cctl = llis_va[0].cctl;
- /* ccfg will be set at physical channel allocation time */
+ llis_va = txd->llis_va;
+ /* The final LLI terminates the LLI. */
+ llis_va[num_llis - 1].lli = 0;
+ /* The final LLI element shall also fire an interrupt. */
+ llis_va[num_llis - 1].cctl |= PL080_CONTROL_TC_IRQ_EN;
#ifdef VERBOSE_DEBUG
{
@@ -969,13 +842,13 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
for (i = 0; i < num_llis; i++) {
dev_vdbg(&pl08x->adev->dev,
- "lli %d @%p: csrc=%08x, cdst=%08x, cctl=%08x, clli=%08x\n",
+ "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].next
+ llis_va[i].lli
);
}
}
@@ -988,14 +861,8 @@ 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)
{
- if (!txd)
- dev_err(&pl08x->adev->dev,
- "%s no descriptor to free\n",
- __func__);
-
/* Free the LLI */
- dma_pool_free(pl08x->pool, txd->llis_va,
- txd->llis_bus);
+ dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
pl08x->pool_ctr--;
@@ -1008,13 +875,12 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
struct pl08x_txd *txdi = NULL;
struct pl08x_txd *next;
- if (!list_empty(&plchan->desc_list)) {
+ if (!list_empty(&plchan->pend_list)) {
list_for_each_entry_safe(txdi,
- next, &plchan->desc_list, node) {
+ next, &plchan->pend_list, node) {
list_del(&txdi->node);
pl08x_free_txd(pl08x, txdi);
}
-
}
}
@@ -1069,6 +935,12 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
return -EBUSY;
}
ch->signal = ret;
+
+ /* Assign the flow control signal to this channel */
+ if (txd->direction == DMA_TO_DEVICE)
+ txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT;
+ else if (txd->direction == DMA_FROM_DEVICE)
+ txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
}
dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n",
@@ -1076,19 +948,54 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
ch->signal,
plchan->name);
+ plchan->phychan_hold++;
plchan->phychan = ch;
return 0;
}
+static void release_phy_channel(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+
+ if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) {
+ pl08x->pd->put_signal(plchan);
+ plchan->phychan->signal = -1;
+ }
+ pl08x_put_phy_channel(pl08x, plchan->phychan);
+ plchan->phychan = NULL;
+}
+
static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan);
+ struct pl08x_txd *txd = to_pl08x_txd(tx);
+ unsigned long flags;
+
+ spin_lock_irqsave(&plchan->lock, flags);
+
+ plchan->chan.cookie += 1;
+ if (plchan->chan.cookie < 0)
+ plchan->chan.cookie = 1;
+ tx->cookie = plchan->chan.cookie;
+
+ /* Put this onto the pending list */
+ list_add_tail(&txd->node, &plchan->pend_list);
+
+ /*
+ * If there was no physical channel available for this memcpy,
+ * stack the request up and indicate that the channel is waiting
+ * for a free physical channel.
+ */
+ if (!plchan->slave && !plchan->phychan) {
+ /* Do this memcpy whenever there is a channel ready */
+ plchan->state = PL08X_CHAN_WAITING;
+ plchan->waiting = txd;
+ } else {
+ plchan->phychan_hold--;
+ }
- atomic_inc(&plchan->last_issued);
- tx->cookie = atomic_read(&plchan->last_issued);
- /* This unlock follows the lock in the prep() function */
- spin_unlock_irqrestore(&plchan->lock, plchan->lockflags);
+ spin_unlock_irqrestore(&plchan->lock, flags);
return tx->cookie;
}
@@ -1102,10 +1009,9 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
}
/*
- * Code accessing dma_async_is_complete() in a tight loop
- * may give problems - could schedule where indicated.
- * If slaves are relying on interrupts to signal completion this
- * function must not be called with interrupts disabled
+ * Code accessing dma_async_is_complete() in a tight loop may give problems.
+ * 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,
@@ -1118,7 +1024,7 @@ pl08x_dma_tx_status(struct dma_chan *chan,
enum dma_status ret;
u32 bytesleft = 0;
- last_used = atomic_read(&plchan->last_issued);
+ last_used = plchan->chan.cookie;
last_complete = plchan->lc;
ret = dma_async_is_complete(cookie, last_complete, last_used);
@@ -1128,13 +1034,9 @@ pl08x_dma_tx_status(struct dma_chan *chan,
}
/*
- * schedule(); could be inserted here
- */
-
- /*
* This cookie not complete yet
*/
- last_used = atomic_read(&plchan->last_issued);
+ last_used = plchan->chan.cookie;
last_complete = plchan->lc;
/* Get number of bytes left in the active transactions and queue */
@@ -1199,37 +1101,35 @@ static const struct burst_table burst_sizes[] = {
},
};
-static void dma_set_runtime_config(struct dma_chan *chan,
- struct dma_slave_config *config)
+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 cctl = 0;
- /* Mask out all except src and dst channel */
- u32 ccfg = cd->ccfg & 0x000003DEU;
- int i = 0;
+ int i;
+
+ if (!plchan->slave)
+ return -EINVAL;
/* Transfer direction */
plchan->runtime_direction = config->direction;
if (config->direction == DMA_TO_DEVICE) {
- plchan->runtime_addr = config->dst_addr;
- cctl |= PL080_CONTROL_SRC_INCR;
- ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ addr = config->dst_addr;
addr_width = config->dst_addr_width;
maxburst = config->dst_maxburst;
} else if (config->direction == DMA_FROM_DEVICE) {
- plchan->runtime_addr = config->src_addr;
- cctl |= PL080_CONTROL_DST_INCR;
- ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ addr = config->src_addr;
addr_width = config->src_addr_width;
maxburst = config->src_maxburst;
} else {
dev_err(&pl08x->adev->dev,
"bad runtime_config: alien transfer direction\n");
- return;
+ return -EINVAL;
}
switch (addr_width) {
@@ -1248,42 +1148,40 @@ static void dma_set_runtime_config(struct dma_chan *chan,
default:
dev_err(&pl08x->adev->dev,
"bad runtime_config: alien address width\n");
- return;
+ return -EINVAL;
}
/*
* Now decide on a maxburst:
- * If this channel will only request single transfers, set
- * this down to ONE element.
+ * 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) {
+ if (plchan->cd->single || maxburst == 0) {
cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
(PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
} else {
- while (i < ARRAY_SIZE(burst_sizes)) {
+ for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
if (burst_sizes[i].burstwords <= maxburst)
break;
- i++;
- }
cctl |= burst_sizes[i].reg;
}
- /* Access the cell in privileged mode, non-bufferable, non-cacheable */
- cctl &= ~PL080_CONTROL_PROT_MASK;
- cctl |= PL080_CONTROL_PROT_SYS;
+ plchan->runtime_addr = addr;
/* Modify the default channel data to fit PrimeCell request */
cd->cctl = cctl;
- cd->ccfg = ccfg;
dev_dbg(&pl08x->adev->dev,
"configured channel %s (%s) for %s, data width %d, "
- "maxburst %d words, LE, CCTL=%08x, CCFG=%08x\n",
+ "maxburst %d words, LE, CCTL=0x%08x\n",
dma_chan_name(chan), plchan->name,
(config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
addr_width,
maxburst,
- cctl, ccfg);
+ cctl);
+
+ return 0;
}
/*
@@ -1293,35 +1191,26 @@ static void dma_set_runtime_config(struct dma_chan *chan,
static void pl08x_issue_pending(struct dma_chan *chan)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
- struct pl08x_driver_data *pl08x = plchan->host;
unsigned long flags;
spin_lock_irqsave(&plchan->lock, flags);
- /* Something is already active */
- if (plchan->at) {
- spin_unlock_irqrestore(&plchan->lock, flags);
- return;
- }
-
- /* Didn't get a physical channel so waiting for it ... */
- if (plchan->state == PL08X_CHAN_WAITING)
+ /* Something is already active, or we're waiting for a channel... */
+ if (plchan->at || plchan->state == PL08X_CHAN_WAITING) {
+ spin_unlock_irqrestore(&plchan->lock, flags);
return;
+ }
/* Take the first element in the queue and execute it */
- if (!list_empty(&plchan->desc_list)) {
+ if (!list_empty(&plchan->pend_list)) {
struct pl08x_txd *next;
- next = list_first_entry(&plchan->desc_list,
+ next = list_first_entry(&plchan->pend_list,
struct pl08x_txd,
node);
list_del(&next->node);
- plchan->at = next;
plchan->state = PL08X_CHAN_RUNNING;
- /* Configure the physical channel for the active txd */
- pl08x_config_phychan_for_txd(plchan);
- pl08x_set_cregs(pl08x, plchan->phychan);
- pl08x_enable_phy_chan(pl08x, plchan->phychan);
+ pl08x_start_txd(plchan, next);
}
spin_unlock_irqrestore(&plchan->lock, flags);
@@ -1330,30 +1219,17 @@ static void pl08x_issue_pending(struct dma_chan *chan)
static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
struct pl08x_txd *txd)
{
- int num_llis;
struct pl08x_driver_data *pl08x = plchan->host;
- int ret;
+ unsigned long flags;
+ int num_llis, ret;
num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
-
- if (!num_llis)
+ if (!num_llis) {
+ kfree(txd);
return -EINVAL;
+ }
- spin_lock_irqsave(&plchan->lock, plchan->lockflags);
-
- /*
- * If this device is not using a circular buffer then
- * queue this new descriptor for transfer.
- * The descriptor for a circular buffer continues
- * to be used until the channel is freed.
- */
- if (txd->cd->circular_buffer)
- dev_err(&pl08x->adev->dev,
- "%s attempting to queue a circular buffer\n",
- __func__);
- else
- list_add_tail(&txd->node,
- &plchan->desc_list);
+ spin_lock_irqsave(&plchan->lock, flags);
/*
* See if we already have a physical channel allocated,
@@ -1362,45 +1238,74 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
ret = prep_phy_channel(plchan, txd);
if (ret) {
/*
- * No physical channel available, we will
- * stack up the memcpy channels until there is a channel
- * available to handle it whereas slave transfers may
- * have been denied due to platform channel muxing restrictions
- * and since there is no guarantee that this will ever be
- * resolved, and since the signal must be aquired AFTER
- * aquiring the physical channel, we will let them be NACK:ed
- * with -EBUSY here. The drivers can alway retry the prep()
- * call if they are eager on doing this using DMA.
+ * No physical channel was available.
+ *
+ * memcpy transfers can be sorted out at submission time.
+ *
+ * Slave transfers may have been denied due to platform
+ * channel muxing restrictions. Since there is no guarantee
+ * that this will ever be resolved, and the signal must be
+ * acquired AFTER acquiring the physical channel, we will let
+ * them be NACK:ed with -EBUSY here. The drivers can retry
+ * the prep() call if they are eager on doing this using DMA.
*/
if (plchan->slave) {
pl08x_free_txd_list(pl08x, plchan);
- spin_unlock_irqrestore(&plchan->lock, plchan->lockflags);
+ pl08x_free_txd(pl08x, txd);
+ spin_unlock_irqrestore(&plchan->lock, flags);
return -EBUSY;
}
- /* Do this memcpy whenever there is a channel ready */
- plchan->state = PL08X_CHAN_WAITING;
- plchan->waiting = txd;
} else
/*
- * Else we're all set, paused and ready to roll,
- * status will switch to PL08X_CHAN_RUNNING when
- * we call issue_pending(). If there is something
- * running on the channel already we don't change
- * its state.
+ * Else we're all set, paused and ready to roll, status
+ * will switch to PL08X_CHAN_RUNNING when we call
+ * issue_pending(). If there is something running on the
+ * channel already we don't change its state.
*/
if (plchan->state == PL08X_CHAN_IDLE)
plchan->state = PL08X_CHAN_PAUSED;
- /*
- * Notice that we leave plchan->lock locked on purpose:
- * it will be unlocked in the subsequent tx_submit()
- * call. This is a consequence of the current API.
- */
+ spin_unlock_irqrestore(&plchan->lock, flags);
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);
+
+ 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);
+
+ /* Always enable error and terminal interrupts */
+ txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK |
+ PL080_CONFIG_TC_IRQ_MASK;
+ }
+ return txd;
+}
+
+/*
* Initialize a descriptor to be used by memcpy submit
*/
static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
@@ -1412,40 +1317,38 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
struct pl08x_txd *txd;
int ret;
- txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
+ txd = pl08x_get_txd(plchan, flags);
if (!txd) {
dev_err(&pl08x->adev->dev,
"%s no memory for descriptor\n", __func__);
return NULL;
}
- dma_async_tx_descriptor_init(&txd->tx, chan);
txd->direction = DMA_NONE;
- txd->srcbus.addr = src;
- txd->dstbus.addr = dest;
+ txd->src_addr = src;
+ txd->dst_addr = dest;
+ txd->len = len;
/* Set platform data for m2m */
- txd->cd = &pl08x->pd->memcpy_channel;
+ txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ txd->cctl = pl08x->pd->memcpy_channel.cctl &
+ ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
+
/* Both to be incremented or the code will break */
- txd->cd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
- txd->tx.tx_submit = pl08x_tx_submit;
- txd->tx.callback = NULL;
- txd->tx.callback_param = NULL;
- txd->len = len;
+ 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);
- INIT_LIST_HEAD(&txd->node);
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
return NULL;
- /*
- * NB: the channel lock is held at this point so tx_submit()
- * must be called in direct succession.
- */
return &txd->tx;
}
-struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
+static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags)
@@ -1453,6 +1356,7 @@ 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;
/*
@@ -1467,14 +1371,12 @@ struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
__func__, sgl->length, plchan->name);
- txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
+ txd = pl08x_get_txd(plchan, flags);
if (!txd) {
dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
return NULL;
}
- dma_async_tx_descriptor_init(&txd->tx, chan);
-
if (direction != plchan->runtime_direction)
dev_err(&pl08x->adev->dev, "%s DMA setup does not match "
"the direction configured for the PrimeCell\n",
@@ -1486,37 +1388,47 @@ 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->srcbus.addr = sgl->dma_address;
+ 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->dstbus.addr = plchan->runtime_addr;
+ txd->dst_addr = plchan->runtime_addr;
else
- txd->dstbus.addr = plchan->cd->addr;
+ txd->dst_addr = plchan->cd->addr;
+ src_buses = pl08x->mem_buses;
+ dst_buses = plchan->cd->periph_buses;
} 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->srcbus.addr = plchan->runtime_addr;
+ txd->src_addr = plchan->runtime_addr;
else
- txd->srcbus.addr = plchan->cd->addr;
- txd->dstbus.addr = sgl->dma_address;
+ txd->src_addr = plchan->cd->addr;
+ txd->dst_addr = sgl->dma_address;
+ src_buses = plchan->cd->periph_buses;
+ dst_buses = pl08x->mem_buses;
} else {
dev_err(&pl08x->adev->dev,
"%s direction unsupported\n", __func__);
return NULL;
}
- txd->cd = plchan->cd;
- txd->tx.tx_submit = pl08x_tx_submit;
- txd->tx.callback = NULL;
- txd->tx.callback_param = NULL;
- txd->len = sgl->length;
- INIT_LIST_HEAD(&txd->node);
+
+ txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
return NULL;
- /*
- * NB: the channel lock is held at this point so tx_submit()
- * must be called in direct succession.
- */
return &txd->tx;
}
@@ -1531,10 +1443,8 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* Controls applicable to inactive channels */
if (cmd == DMA_SLAVE_CONFIG) {
- dma_set_runtime_config(chan,
- (struct dma_slave_config *)
- arg);
- return 0;
+ return dma_set_runtime_config(chan,
+ (struct dma_slave_config *)arg);
}
/*
@@ -1552,22 +1462,14 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
plchan->state = PL08X_CHAN_IDLE;
if (plchan->phychan) {
- pl08x_stop_phy_chan(plchan->phychan);
+ pl08x_terminate_phy_chan(pl08x, plchan->phychan);
/*
* Mark physical channel as free and free any slave
* signal
*/
- if ((plchan->phychan->signal >= 0) &&
- pl08x->pd->put_signal) {
- pl08x->pd->put_signal(plchan);
- plchan->phychan->signal = -1;
- }
- pl08x_put_phy_channel(pl08x, plchan->phychan);
- plchan->phychan = NULL;
+ release_phy_channel(plchan);
}
- /* Stop any pending tasklet */
- tasklet_disable(&plchan->tasklet);
/* Dequeue jobs and free LLIs */
if (plchan->at) {
pl08x_free_txd(pl08x, plchan->at);
@@ -1609,10 +1511,9 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
/*
* Just check that the device is there and active
- * TODO: turn this bit on/off depending on the number of
- * physical channels actually used, if it is zero... well
- * shut it off. That will save some power. Cut the clock
- * at the same time.
+ * TODO: turn this bit on/off depending on the number of physical channels
+ * actually used, if it is zero... well shut it off. That will save some
+ * power. Cut the clock at the same time.
*/
static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
{
@@ -1620,78 +1521,66 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
val = readl(pl08x->base + PL080_CONFIG);
val &= ~(PL080_CONFIG_M2_BE | PL080_CONFIG_M1_BE | PL080_CONFIG_ENABLE);
- /* We implictly clear bit 1 and that means little-endian mode */
+ /* We implicitly clear bit 1 and that means little-endian mode */
val |= PL080_CONFIG_ENABLE;
writel(val, pl08x->base + PL080_CONFIG);
}
+static void pl08x_unmap_buffers(struct pl08x_txd *txd)
+{
+ struct device *dev = txd->tx.chan->device->dev;
+
+ 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);
+ }
+ 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);
+ else
+ dma_unmap_page(dev, txd->dst_addr, txd->len,
+ DMA_FROM_DEVICE);
+ }
+}
+
static void pl08x_tasklet(unsigned long data)
{
struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data;
- struct pl08x_phy_chan *phychan = plchan->phychan;
struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_txd *txd;
+ unsigned long flags;
- if (!plchan)
- BUG();
-
- spin_lock(&plchan->lock);
-
- if (plchan->at) {
- dma_async_tx_callback callback =
- plchan->at->tx.callback;
- void *callback_param =
- plchan->at->tx.callback_param;
-
- /*
- * Update last completed
- */
- plchan->lc =
- (plchan->at->tx.cookie);
-
- /*
- * Callback to signal completion
- */
- if (callback)
- callback(callback_param);
+ spin_lock_irqsave(&plchan->lock, flags);
- /*
- * Device callbacks should NOT clear
- * the current transaction on the channel
- * Linus: sometimes they should?
- */
- if (!plchan->at)
- BUG();
+ txd = plchan->at;
+ plchan->at = NULL;
- /*
- * Free the descriptor if it's not for a device
- * using a circular buffer
- */
- if (!plchan->at->cd->circular_buffer) {
- pl08x_free_txd(pl08x, plchan->at);
- plchan->at = NULL;
- }
- /*
- * else descriptor for circular
- * buffers only freed when
- * client has disabled dma
- */
+ if (txd) {
+ /* Update last completed */
+ plchan->lc = txd->tx.cookie;
}
- /*
- * If a new descriptor is queued, set it up
- * plchan->at is NULL here
- */
- if (!list_empty(&plchan->desc_list)) {
+
+ /* If a new descriptor is queued, set it up plchan->at is NULL here */
+ if (!list_empty(&plchan->pend_list)) {
struct pl08x_txd *next;
- next = list_first_entry(&plchan->desc_list,
+ next = list_first_entry(&plchan->pend_list,
struct pl08x_txd,
node);
list_del(&next->node);
- plchan->at = next;
- /* Configure the physical channel for the next txd */
- pl08x_config_phychan_for_txd(plchan);
- pl08x_set_cregs(pl08x, plchan->phychan);
- pl08x_enable_phy_chan(pl08x, plchan->phychan);
+
+ pl08x_start_txd(plchan, next);
+ } else if (plchan->phychan_hold) {
+ /*
+ * This channel is still in use - we have a new txd being
+ * prepared and will soon be queued. Don't give up the
+ * physical channel.
+ */
} else {
struct pl08x_dma_chan *waiting = NULL;
@@ -1699,20 +1588,14 @@ static void pl08x_tasklet(unsigned long data)
* No more jobs, so free up the physical channel
* Free any allocated signal on slave transfers too
*/
- if ((phychan->signal >= 0) && pl08x->pd->put_signal) {
- pl08x->pd->put_signal(plchan);
- phychan->signal = -1;
- }
- pl08x_put_phy_channel(pl08x, phychan);
- plchan->phychan = NULL;
+ release_phy_channel(plchan);
plchan->state = PL08X_CHAN_IDLE;
/*
- * And NOW before anyone else can grab that free:d
- * up physical channel, see if there is some memcpy
- * pending that seriously needs to start because of
- * being stacked up while we were choking the
- * physical channels with data.
+ * And NOW before anyone else can grab that free:d up
+ * physical channel, see if there is some memcpy pending
+ * that seriously needs to start because of being stacked
+ * up while we were choking the physical channels with data.
*/
list_for_each_entry(waiting, &pl08x->memcpy.channels,
chan.device_node) {
@@ -1724,6 +1607,7 @@ static void pl08x_tasklet(unsigned long data)
ret = prep_phy_channel(waiting,
waiting->waiting);
BUG_ON(ret);
+ waiting->phychan_hold--;
waiting->state = PL08X_CHAN_RUNNING;
waiting->waiting = NULL;
pl08x_issue_pending(&waiting->chan);
@@ -1732,7 +1616,25 @@ static void pl08x_tasklet(unsigned long data)
}
}
- spin_unlock(&plchan->lock);
+ spin_unlock_irqrestore(&plchan->lock, flags);
+
+ if (txd) {
+ dma_async_tx_callback callback = txd->tx.callback;
+ void *callback_param = txd->tx.callback_param;
+
+ /* Don't try to unmap buffers on slave channels */
+ if (!plchan->slave)
+ pl08x_unmap_buffers(txd);
+
+ /* Free the descriptor */
+ spin_lock_irqsave(&plchan->lock, flags);
+ pl08x_free_txd(pl08x, txd);
+ spin_unlock_irqrestore(&plchan->lock, flags);
+
+ /* Callback to signal completion */
+ if (callback)
+ callback(callback_param);
+ }
}
static irqreturn_t pl08x_irq(int irq, void *dev)
@@ -1744,9 +1646,7 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
val = readl(pl08x->base + PL080_ERR_STATUS);
if (val) {
- /*
- * An error interrupt (on one or more channels)
- */
+ /* An error interrupt (on one or more channels) */
dev_err(&pl08x->adev->dev,
"%s error interrupt, register value 0x%08x\n",
__func__, val);
@@ -1770,9 +1670,7 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
mask |= (1 << i);
}
}
- /*
- * Clear only the terminal interrupts on channels we processed
- */
+ /* Clear only the terminal interrupts on channels we processed */
writel(mask, pl08x->base + PL080_TC_CLEAR);
return mask ? IRQ_HANDLED : IRQ_NONE;
@@ -1791,6 +1689,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
int i;
INIT_LIST_HEAD(&dmadev->channels);
+
/*
* Register as many many memcpy as we have physical channels,
* we won't always be able to use all but the code will have
@@ -1819,16 +1718,23 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
return -ENOMEM;
}
}
+ if (chan->cd->circular_buffer) {
+ dev_err(&pl08x->adev->dev,
+ "channel %s: circular buffers not supported\n",
+ chan->name);
+ kfree(chan);
+ continue;
+ }
dev_info(&pl08x->adev->dev,
"initialize virtual channel \"%s\"\n",
chan->name);
chan->chan.device = dmadev;
- atomic_set(&chan->last_issued, 0);
- chan->lc = atomic_read(&chan->last_issued);
+ chan->chan.cookie = 0;
+ chan->lc = 0;
spin_lock_init(&chan->lock);
- INIT_LIST_HEAD(&chan->desc_list);
+ INIT_LIST_HEAD(&chan->pend_list);
tasklet_init(&chan->tasklet, pl08x_tasklet,
(unsigned long) chan);
@@ -1898,7 +1804,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "CHANNEL:\tSTATE:\n");
seq_printf(s, "--------\t------\n");
list_for_each_entry(chan, &pl08x->memcpy.channels, chan.device_node) {
- seq_printf(s, "%s\t\t\%s\n", chan->name,
+ seq_printf(s, "%s\t\t%s\n", chan->name,
pl08x_state_str(chan->state));
}
@@ -1906,7 +1812,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "CHANNEL:\tSTATE:\n");
seq_printf(s, "--------\t------\n");
list_for_each_entry(chan, &pl08x->slave.channels, chan.device_node) {
- seq_printf(s, "%s\t\t\%s\n", chan->name,
+ seq_printf(s, "%s\t\t%s\n", chan->name,
pl08x_state_str(chan->state));
}
@@ -1939,10 +1845,10 @@ static inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
}
#endif
-static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
+static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
{
struct pl08x_driver_data *pl08x;
- struct vendor_data *vd = id->data;
+ const struct vendor_data *vd = id->data;
int ret = 0;
int i;
@@ -1990,6 +1896,14 @@ static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
pl08x->adev = adev;
pl08x->vd = vd;
+ /* By default, AHB1 only. If dualmaster, from platform */
+ pl08x->lli_buses = PL08X_AHB1;
+ pl08x->mem_buses = PL08X_AHB1;
+ if (pl08x->vd->dualmaster) {
+ pl08x->lli_buses = pl08x->pd->lli_buses;
+ pl08x->mem_buses = pl08x->pd->mem_buses;
+ }
+
/* A DMA memory pool for LLIs, align on 1-byte boundary */
pl08x->pool = dma_pool_create(DRIVER_NAME, &pl08x->adev->dev,
PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, 0);
@@ -2009,14 +1923,12 @@ static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
/* Turn on the PL08x */
pl08x_ensure_on(pl08x);
- /*
- * Attach the interrupt handler
- */
+ /* Attach the interrupt handler */
writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
writel(0x000000FF, pl08x->base + PL080_TC_CLEAR);
ret = request_irq(adev->irq[0], pl08x_irq, IRQF_DISABLED,
- vd->name, pl08x);
+ DRIVER_NAME, pl08x);
if (ret) {
dev_err(&adev->dev, "%s failed to request interrupt %d\n",
__func__, adev->irq[0]);
@@ -2087,8 +1999,9 @@ static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
amba_set_drvdata(adev, pl08x);
init_pl08x_debugfs(pl08x);
- dev_info(&pl08x->adev->dev, "ARM(R) %s DMA block initialized @%08x\n",
- vd->name, adev->res.start);
+ 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]);
return 0;
out_no_slave_reg:
@@ -2115,13 +2028,11 @@ out_no_pl08x:
/* PL080 has 8 channels and the PL080 have just 2 */
static struct vendor_data vendor_pl080 = {
- .name = "PL080",
.channels = 8,
.dualmaster = true,
};
static struct vendor_data vendor_pl081 = {
- .name = "PL081",
.channels = 2,
.dualmaster = false,
};
@@ -2160,7 +2071,7 @@ static int __init pl08x_init(void)
retval = amba_driver_register(&pl08x_amba_driver);
if (retval)
printk(KERN_WARNING DRIVER_NAME
- "failed to register as an amba device (%d)\n",
+ "failed to register as an AMBA device (%d)\n",
retval);
return retval;
}
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index a0f3e6a06e06..3d7d705f026f 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -253,7 +253,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
/* move myself to free_list */
list_move(&desc->desc_node, &atchan->free_list);
- /* unmap dma addresses */
+ /* unmap dma addresses (not on slave channels) */
if (!atchan->chan_common.private) {
struct device *parent = chan2parent(&atchan->chan_common);
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
@@ -583,7 +583,6 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
desc->lli.ctrlb = ctrlb;
desc->txd.cookie = 0;
- async_tx_ack(&desc->txd);
if (!first) {
first = desc;
@@ -604,7 +603,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
/* set end-of-link to the last link descriptor of list*/
set_desc_eol(desc);
- desc->txd.flags = flags; /* client is in control of this ack */
+ first->txd.flags = flags; /* client is in control of this ack */
return &first->txd;
@@ -670,7 +669,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (!desc)
goto err_desc_get;
- mem = sg_phys(sg);
+ mem = sg_dma_address(sg);
len = sg_dma_len(sg);
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
@@ -712,7 +711,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (!desc)
goto err_desc_get;
- mem = sg_phys(sg);
+ mem = sg_dma_address(sg);
len = sg_dma_len(sg);
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
@@ -722,7 +721,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
desc->lli.daddr = mem;
desc->lli.ctrla = ctrla
| ATC_DST_WIDTH(mem_width)
- | len >> mem_width;
+ | len >> reg_width;
desc->lli.ctrlb = ctrlb;
if (!first) {
@@ -749,8 +748,8 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
first->txd.cookie = -EBUSY;
first->len = total_len;
- /* last link descriptor of list is responsible of flags */
- prev->txd.flags = flags; /* client is in control of this ack */
+ /* first link descriptor of list is responsible of flags */
+ first->txd.flags = flags; /* client is in control of this ack */
return &first->txd;
@@ -854,11 +853,11 @@ static void atc_issue_pending(struct dma_chan *chan)
dev_vdbg(chan2dev(chan), "issue_pending\n");
+ spin_lock_bh(&atchan->lock);
if (!atc_chan_is_enabled(atchan)) {
- spin_lock_bh(&atchan->lock);
atc_advance_work(atchan);
- spin_unlock_bh(&atchan->lock);
}
+ spin_unlock_bh(&atchan->lock);
}
/**
@@ -1210,7 +1209,7 @@ static int __init at_dma_init(void)
{
return platform_driver_probe(&at_dma_driver, at_dma_probe);
}
-module_init(at_dma_init);
+subsys_initcall(at_dma_init);
static void __exit at_dma_exit(void)
{
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index a6656834f0ff..00deabd9a04b 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -849,7 +849,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
/* Must clear TC interrupt before calling
* dma_tc_handle
- * in case tc_handle initate a new dma job
+ * in case tc_handle initiate a new dma job
*/
__set_bit(i, virtbase + COH901318_TC_INT_CLEAR1);
@@ -894,7 +894,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
}
/* Must clear TC interrupt before calling
* dma_tc_handle
- * in case tc_handle initate a new dma job
+ * in case tc_handle initiate a new dma job
*/
__set_bit(i, virtbase + COH901318_TC_INT_CLEAR2);
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 286c3ac6bdcc..e3854a8f0de0 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -1,7 +1,7 @@
/*
* Freescale MPC85xx, MPC83xx DMA Engine support
*
- * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All rights reserved.
*
* Author:
* Zhang Wei <wei.zhang@freescale.com>, Jul 2007
@@ -50,9 +50,11 @@ static void dma_init(struct fsldma_chan *chan)
* EIE - Error interrupt enable
* EOSIE - End of segments interrupt enable (basic mode)
* EOLNIE - End of links interrupt enable
+ * BWC - Bandwidth sharing among channels
*/
- DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EIE
- | FSL_DMA_MR_EOLNIE | FSL_DMA_MR_EOSIE, 32);
+ DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC
+ | FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE
+ | FSL_DMA_MR_EOSIE, 32);
break;
case FSL_DMA_IP_83XX:
/* Set the channel to below modes:
@@ -1279,8 +1281,7 @@ static void fsl_dma_chan_remove(struct fsldma_chan *chan)
kfree(chan);
}
-static int __devinit fsldma_of_probe(struct platform_device *op,
- const struct of_device_id *match)
+static int __devinit fsldma_of_probe(struct platform_device *op)
{
struct fsldma_device *fdev;
struct device_node *child;
@@ -1322,6 +1323,8 @@ static int __devinit fsldma_of_probe(struct platform_device *op,
fdev->common.device_control = fsl_dma_device_control;
fdev->common.dev = &op->dev;
+ dma_set_mask(&(op->dev), DMA_BIT_MASK(36));
+
dev_set_drvdata(&op->dev, fdev);
/*
@@ -1410,20 +1413,13 @@ static struct of_platform_driver fsldma_of_driver = {
static __init int fsldma_init(void)
{
- int ret;
-
pr_info("Freescale Elo / Elo Plus DMA driver\n");
-
- ret = of_register_platform_driver(&fsldma_of_driver);
- if (ret)
- pr_err("fsldma: failed to register platform driver\n");
-
- return ret;
+ return platform_driver_register(&fsldma_of_driver);
}
static void __exit fsldma_exit(void)
{
- of_unregister_platform_driver(&fsldma_of_driver);
+ platform_driver_unregister(&fsldma_of_driver);
}
subsys_initcall(fsldma_init);
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index cb4d6ff51597..ba9f403c0fbe 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All rights reserved.
*
* Author:
* Zhang Wei <wei.zhang@freescale.com>, Jul 2007
@@ -36,6 +36,13 @@
#define FSL_DMA_MR_DAHE 0x00002000
#define FSL_DMA_MR_SAHE 0x00001000
+/*
+ * Bandwidth/pause control determines how many bytes a given
+ * channel is allowed to transfer before the DMA engine pauses
+ * the current channel and switches to the next channel
+ */
+#define FSL_DMA_MR_BWC 0x08000000
+
/* Special MR definition for MPC8349 */
#define FSL_DMA_MR_EOTIE 0x00000080
#define FSL_DMA_MR_PRC_RM 0x00000800
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index f629e4961af5..e18eaabe92b9 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -49,6 +49,7 @@ struct imxdma_channel {
struct imxdma_engine {
struct device *dev;
+ struct device_dma_parameters dma_parms;
struct dma_device dma_device;
struct imxdma_channel channel[MAX_DMA_CHANNELS];
};
@@ -242,6 +243,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
else
dmamode = DMA_MODE_WRITE;
+ switch (imxdmac->word_size) {
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ if (sgl->length & 3 || sgl->dma_address & 3)
+ return NULL;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ if (sgl->length & 1 || sgl->dma_address & 1)
+ return NULL;
+ break;
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ break;
+ default:
+ return NULL;
+ }
+
ret = imx_dma_setup_sg(imxdmac->imxdma_channel, sgl, sg_len,
dma_length, imxdmac->per_address, dmamode);
if (ret)
@@ -329,6 +345,9 @@ static int __init imxdma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&imxdma->dma_device.channels);
+ dma_cap_set(DMA_SLAVE, imxdma->dma_device.cap_mask);
+ dma_cap_set(DMA_CYCLIC, imxdma->dma_device.cap_mask);
+
/* Initialize channel parameters */
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
struct imxdma_channel *imxdmac = &imxdma->channel[i];
@@ -346,11 +365,7 @@ static int __init imxdma_probe(struct platform_device *pdev)
imxdmac->imxdma = imxdma;
spin_lock_init(&imxdmac->lock);
- dma_cap_set(DMA_SLAVE, imxdma->dma_device.cap_mask);
- dma_cap_set(DMA_CYCLIC, imxdma->dma_device.cap_mask);
-
imxdmac->chan.device = &imxdma->dma_device;
- imxdmac->chan.chan_id = i;
imxdmac->channel = i;
/* Add the channel to the DMAC list */
@@ -370,6 +385,9 @@ static int __init imxdma_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, imxdma);
+ imxdma->dma_device.dev->dma_parms = &imxdma->dma_parms;
+ dma_set_max_seg_size(imxdma->dma_device.dev, 0xffffff);
+
ret = dma_async_device_register(&imxdma->dma_device);
if (ret) {
dev_err(&pdev->dev, "unable to register\n");
@@ -379,7 +397,7 @@ static int __init imxdma_probe(struct platform_device *pdev)
return 0;
err_init:
- while (i-- >= 0) {
+ while (--i >= 0) {
struct imxdma_channel *imxdmac = &imxdma->channel[i];
imx_dma_free(imxdmac->imxdma_channel);
}
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 0834323a0599..b6d1455fa936 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -230,7 +230,7 @@ struct sdma_engine;
* struct sdma_channel - housekeeping for a SDMA channel
*
* @sdma pointer to the SDMA engine for this channel
- * @channel the channel number, matches dmaengine chan_id
+ * @channel the channel number, matches dmaengine chan_id + 1
* @direction transfer type. Needed for setting SDMA script
* @peripheral_type Peripheral type. Needed for setting SDMA script
* @event_id0 aka dma request line
@@ -273,50 +273,6 @@ struct sdma_channel {
#define MXC_SDMA_MIN_PRIORITY 1
#define MXC_SDMA_MAX_PRIORITY 7
-/**
- * struct sdma_script_start_addrs - SDMA script start pointers
- *
- * start addresses of the different functions in the physical
- * address space of the SDMA engine.
- */
-struct sdma_script_start_addrs {
- u32 ap_2_ap_addr;
- u32 ap_2_bp_addr;
- u32 ap_2_ap_fixed_addr;
- u32 bp_2_ap_addr;
- u32 loopback_on_dsp_side_addr;
- u32 mcu_interrupt_only_addr;
- u32 firi_2_per_addr;
- u32 firi_2_mcu_addr;
- u32 per_2_firi_addr;
- u32 mcu_2_firi_addr;
- u32 uart_2_per_addr;
- u32 uart_2_mcu_addr;
- u32 per_2_app_addr;
- u32 mcu_2_app_addr;
- u32 per_2_per_addr;
- u32 uartsh_2_per_addr;
- u32 uartsh_2_mcu_addr;
- u32 per_2_shp_addr;
- u32 mcu_2_shp_addr;
- u32 ata_2_mcu_addr;
- u32 mcu_2_ata_addr;
- u32 app_2_per_addr;
- u32 app_2_mcu_addr;
- u32 shp_2_per_addr;
- u32 shp_2_mcu_addr;
- u32 mshc_2_mcu_addr;
- u32 mcu_2_mshc_addr;
- u32 spdif_2_mcu_addr;
- u32 mcu_2_spdif_addr;
- u32 asrc_2_mcu_addr;
- u32 ext_mem_2_ipu_addr;
- u32 descrambler_addr;
- u32 dptc_dvfs_addr;
- u32 utra_addr;
- u32 ram_code_start_addr;
-};
-
#define SDMA_FIRMWARE_MAGIC 0x414d4453
/**
@@ -345,6 +301,7 @@ struct sdma_firmware_header {
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;
@@ -493,7 +450,7 @@ static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
if (bd->mode.status & BD_RROR)
sdmac->status = DMA_ERROR;
else
- sdmac->status = DMA_SUCCESS;
+ sdmac->status = DMA_IN_PROGRESS;
bd->mode.status |= BD_DONE;
sdmac->buf_tail++;
@@ -814,15 +771,15 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
__raw_writel(1 << channel, sdma->regs + SDMA_H_START);
}
-static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdma)
+static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac)
{
- dma_cookie_t cookie = sdma->chan.cookie;
+ dma_cookie_t cookie = sdmac->chan.cookie;
if (++cookie < 0)
cookie = 1;
- sdma->chan.cookie = cookie;
- sdma->desc.cookie = cookie;
+ sdmac->chan.cookie = cookie;
+ sdmac->desc.cookie = cookie;
return cookie;
}
@@ -842,7 +799,7 @@ static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx)
cookie = sdma_assign_cookie(sdmac);
- sdma_enable_channel(sdma, tx->chan->chan_id);
+ sdma_enable_channel(sdma, sdmac->channel);
spin_unlock_irq(&sdmac->lock);
@@ -855,10 +812,6 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
struct imx_dma_data *data = chan->private;
int prio, ret;
- /* No need to execute this for internal channel 0 */
- if (chan->chan_id == 0)
- return 0;
-
if (!data)
return -EINVAL;
@@ -923,7 +876,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
int ret, i, count;
- int channel = chan->chan_id;
+ int channel = sdmac->channel;
struct scatterlist *sg;
if (sdmac->status == DMA_IN_PROGRESS)
@@ -951,7 +904,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
struct sdma_buffer_descriptor *bd = &sdmac->bd[i];
int param;
- bd->buffer_addr = sgl->dma_address;
+ bd->buffer_addr = sg->dma_address;
count = sg->length;
@@ -968,22 +921,33 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
ret = -EINVAL;
goto err_out;
}
- if (sdmac->word_size == DMA_SLAVE_BUSWIDTH_4_BYTES)
+
+ switch (sdmac->word_size) {
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
bd->mode.command = 0;
- else
- bd->mode.command = sdmac->word_size;
+ if (count & 3 || sg->dma_address & 3)
+ return NULL;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ bd->mode.command = 2;
+ if (count & 1 || sg->dma_address & 1)
+ return NULL;
+ break;
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ bd->mode.command = 1;
+ break;
+ default:
+ return NULL;
+ }
param = BD_DONE | BD_EXTD | BD_CONT;
- if (sdmac->flags & IMX_DMA_SG_LOOP) {
+ if (i + 1 == sg_len) {
param |= BD_INTR;
- if (i + 1 == sg_len)
- param |= BD_WRAP;
+ param |= BD_LAST;
+ param &= ~BD_CONT;
}
- if (i + 1 == sg_len)
- param |= BD_INTR;
-
dev_dbg(sdma->dev, "entry %d: count: %d dma: 0x%08x %s%s\n",
i, count, sg->dma_address,
param & BD_WRAP ? "wrap" : "",
@@ -997,6 +961,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
return &sdmac->desc;
err_out:
+ sdmac->status = DMA_ERROR;
return NULL;
}
@@ -1007,7 +972,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
int num_periods = buf_len / period_len;
- int channel = chan->chan_id;
+ int channel = sdmac->channel;
int ret, i = 0, buf = 0;
dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel);
@@ -1110,14 +1075,12 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
dma_cookie_t last_used;
- enum dma_status ret;
last_used = chan->cookie;
- ret = dma_async_is_complete(cookie, sdmac->last_completed, last_used);
dma_set_tx_state(txstate, sdmac->last_completed, last_used, 0);
- return ret;
+ return sdmac->status;
}
static void sdma_issue_pending(struct dma_chan *chan)
@@ -1127,8 +1090,74 @@ static void sdma_issue_pending(struct dma_chan *chan)
*/
}
-static int __init sdma_init(struct sdma_engine *sdma,
- void *ram_code, int ram_code_size)
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34
+
+static void sdma_add_scripts(struct sdma_engine *sdma,
+ const struct sdma_script_start_addrs *addr)
+{
+ s32 *addr_arr = (u32 *)addr;
+ s32 *saddr_arr = (u32 *)sdma->script_addrs;
+ int i;
+
+ for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++)
+ if (addr_arr[i] > 0)
+ saddr_arr[i] = addr_arr[i];
+}
+
+static int __init sdma_get_firmware(struct sdma_engine *sdma,
+ const char *cpu_name, int to_version)
+{
+ const struct firmware *fw;
+ char *fwname;
+ 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;
+ }
+ kfree(fwname);
+
+ if (fw->size < sizeof(*header))
+ goto err_firmware;
+
+ header = (struct sdma_firmware_header *)fw->data;
+
+ if (header->magic != SDMA_FIRMWARE_MAGIC)
+ goto err_firmware;
+ if (header->ram_code_start + header->ram_code_size > fw->size)
+ goto err_firmware;
+
+ addr = (void *)header + header->script_addrs_start;
+ ram_code = (void *)header + header->ram_code_start;
+
+ clk_enable(sdma->clk);
+ /* download the RAM image for SDMA */
+ sdma_load_script(sdma, ram_code,
+ header->ram_code_size,
+ addr->ram_code_start_addr);
+ clk_disable(sdma->clk);
+
+ sdma_add_scripts(sdma, addr);
+
+ dev_info(sdma->dev, "loaded firmware %d.%d\n",
+ header->version_major,
+ header->version_minor);
+
+err_firmware:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int __init sdma_init(struct sdma_engine *sdma)
{
int i, ret;
dma_addr_t ccb_phys;
@@ -1192,11 +1221,6 @@ static int __init sdma_init(struct sdma_engine *sdma,
__raw_writel(ccb_phys, sdma->regs + SDMA_H_C0PTR);
- /* download the RAM image for SDMA */
- sdma_load_script(sdma, ram_code,
- ram_code_size,
- sdma->script_addrs->ram_code_start_addr);
-
/* Set bits of CONFIG register with given context switching mode */
__raw_writel(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
@@ -1216,16 +1240,10 @@ err_dma_alloc:
static int __init sdma_probe(struct platform_device *pdev)
{
int ret;
- const struct firmware *fw;
- const struct sdma_firmware_header *header;
- const struct sdma_script_start_addrs *addr;
int irq;
- unsigned short *ram_code;
struct resource *iores;
struct sdma_platform_data *pdata = pdev->dev.platform_data;
- char *fwname;
int i;
- dma_cap_mask_t mask;
struct sdma_engine *sdma;
sdma = kzalloc(sizeof(*sdma), GFP_KERNEL);
@@ -1262,41 +1280,15 @@ static int __init sdma_probe(struct platform_device *pdev)
if (ret)
goto err_request_irq;
- fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin",
- pdata->cpu_name, pdata->to_version);
- if (!fwname) {
- ret = -ENOMEM;
- goto err_cputype;
- }
-
- ret = request_firmware(&fw, fwname, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "request firmware \"%s\" failed with %d\n",
- fwname, ret);
- kfree(fwname);
- goto err_cputype;
- }
- kfree(fwname);
-
- if (fw->size < sizeof(*header))
- goto err_firmware;
-
- header = (struct sdma_firmware_header *)fw->data;
-
- if (header->magic != SDMA_FIRMWARE_MAGIC)
- goto err_firmware;
- if (header->ram_code_start + header->ram_code_size > fw->size)
- goto err_firmware;
-
- addr = (void *)header + header->script_addrs_start;
- ram_code = (void *)header + header->ram_code_start;
- sdma->script_addrs = kmalloc(sizeof(*addr), GFP_KERNEL);
+ sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
if (!sdma->script_addrs)
- goto err_firmware;
- memcpy(sdma->script_addrs, addr, sizeof(*addr));
+ goto err_alloc;
sdma->version = pdata->sdma_version;
+ dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask);
+ dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask);
+
INIT_LIST_HEAD(&sdma->dma_device.channels);
/* Initialize channel parameters */
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
@@ -1305,21 +1297,28 @@ static int __init sdma_probe(struct platform_device *pdev)
sdmac->sdma = sdma;
spin_lock_init(&sdmac->lock);
- dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask);
- dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask);
-
sdmac->chan.device = &sdma->dma_device;
- sdmac->chan.chan_id = i;
sdmac->channel = i;
- /* Add the channel to the DMAC list */
- list_add_tail(&sdmac->chan.device_node, &sdma->dma_device.channels);
+ /*
+ * Add the channel to the DMAC list. Do not add channel 0 though
+ * because we need it internally in the SDMA driver. This also means
+ * that channel 0 in dmaengine counting matches sdma channel 1.
+ */
+ if (i)
+ list_add_tail(&sdmac->chan.device_node,
+ &sdma->dma_device.channels);
}
- ret = sdma_init(sdma, ram_code, header->ram_code_size);
+ ret = sdma_init(sdma);
if (ret)
goto err_init;
+ if (pdata->script_addrs)
+ sdma_add_scripts(sdma, pdata->script_addrs);
+
+ sdma_get_firmware(sdma, pdata->cpu_name, pdata->to_version);
+
sdma->dma_device.dev = &pdev->dev;
sdma->dma_device.device_alloc_chan_resources = sdma_alloc_chan_resources;
@@ -1329,6 +1328,8 @@ static int __init sdma_probe(struct platform_device *pdev)
sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic;
sdma->dma_device.device_control = sdma_control;
sdma->dma_device.device_issue_pending = sdma_issue_pending;
+ sdma->dma_device.dev->dma_parms = &sdma->dma_parms;
+ dma_set_max_seg_size(sdma->dma_device.dev, 65535);
ret = dma_async_device_register(&sdma->dma_device);
if (ret) {
@@ -1336,26 +1337,13 @@ static int __init sdma_probe(struct platform_device *pdev)
goto err_init;
}
- dev_info(&pdev->dev, "initialized (firmware %d.%d)\n",
- header->version_major,
- header->version_minor);
-
- /* request channel 0. This is an internal control channel
- * to the SDMA engine and not available to clients.
- */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_request_channel(mask, NULL, NULL);
-
- release_firmware(fw);
+ dev_info(sdma->dev, "initialized\n");
return 0;
err_init:
kfree(sdma->script_addrs);
-err_firmware:
- release_firmware(fw);
-err_cputype:
+err_alloc:
free_irq(irq, sdma);
err_request_irq:
iounmap(sdma->regs);
@@ -1366,7 +1354,7 @@ err_clk:
err_request_region:
err_irq:
kfree(sdma);
- return 0;
+ return ret;
}
static int __exit sdma_remove(struct platform_device *pdev)
@@ -1385,7 +1373,7 @@ static int __init sdma_module_init(void)
{
return platform_driver_probe(&sdma_driver, sdma_probe);
}
-subsys_initcall(sdma_module_init);
+module_init(sdma_module_init);
MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
MODULE_DESCRIPTION("i.MX SDMA driver");
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index 338bc4eed1f3..798f46a4590d 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -664,11 +664,20 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_memcpy(
/*calculate CTL_LO*/
ctl_lo.ctl_lo = 0;
ctl_lo.ctlx.int_en = 1;
- ctl_lo.ctlx.dst_tr_width = mids->dma_slave.dst_addr_width;
- ctl_lo.ctlx.src_tr_width = mids->dma_slave.src_addr_width;
ctl_lo.ctlx.dst_msize = mids->dma_slave.src_maxburst;
ctl_lo.ctlx.src_msize = mids->dma_slave.dst_maxburst;
+ /*
+ * Here we need some translation from "enum dma_slave_buswidth"
+ * to the format for our dma controller
+ * standard intel_mid_dmac's format
+ * 1 Byte 0b000
+ * 2 Bytes 0b001
+ * 4 Bytes 0b010
+ */
+ ctl_lo.ctlx.dst_tr_width = mids->dma_slave.dst_addr_width / 2;
+ ctl_lo.ctlx.src_tr_width = mids->dma_slave.src_addr_width / 2;
+
if (mids->cfg_mode == LNW_DMA_MEM_TO_MEM) {
ctl_lo.ctlx.tt_fc = 0;
ctl_lo.ctlx.sinc = 0;
@@ -746,8 +755,18 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg(
BUG_ON(!mids);
if (!midc->dma->pimr_mask) {
- pr_debug("MDMA: SG list is not supported by this controller\n");
- return NULL;
+ /* We can still handle sg list with only one item */
+ if (sg_len == 1) {
+ txd = intel_mid_dma_prep_memcpy(chan,
+ mids->dma_slave.dst_addr,
+ mids->dma_slave.src_addr,
+ sgl->length,
+ flags);
+ return txd;
+ } else {
+ pr_warn("MDMA: SG list is not supported by this controller\n");
+ return NULL;
+ }
}
pr_debug("MDMA: SG Length = %d, direction = %d, Flags = %#lx\n",
@@ -758,6 +777,7 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg(
pr_err("MDMA: Prep memcpy failed\n");
return NULL;
}
+
desc = to_intel_mid_dma_desc(txd);
desc->dirn = direction;
ctl_lo.ctl_lo = desc->ctl_lo;
@@ -1021,11 +1041,6 @@ static irqreturn_t intel_mid_dma_interrupt(int irq, void *data)
/*DMA Interrupt*/
pr_debug("MDMA:Got an interrupt on irq %d\n", irq);
- if (!mid) {
- pr_err("ERR_MDMA:null pointer mid\n");
- return -EINVAL;
- }
-
pr_debug("MDMA: Status %x, Mask %x\n", tfr_status, mid->intr_mask);
tfr_status &= mid->intr_mask;
if (tfr_status) {
@@ -1060,8 +1075,8 @@ static irqreturn_t intel_mid_dma_interrupt2(int irq, void *data)
* mid_setup_dma - Setup the DMA controller
* @pdev: Controller PCI device structure
*
- * Initilize the DMA controller, channels, registers with DMA engine,
- * ISR. Initilize DMA controller channels.
+ * Initialize the DMA controller, channels, registers with DMA engine,
+ * ISR. Initialize DMA controller channels.
*/
static int mid_setup_dma(struct pci_dev *pdev)
{
@@ -1075,7 +1090,6 @@ static int mid_setup_dma(struct pci_dev *pdev)
if (NULL == dma->dma_pool) {
pr_err("ERR_MDMA:pci_pool_create failed\n");
err = -ENOMEM;
- kfree(dma);
goto err_dma_pool;
}
@@ -1186,7 +1200,6 @@ err_engine:
free_irq(pdev->irq, dma);
err_irq:
pci_pool_destroy(dma->dma_pool);
- kfree(dma);
err_dma_pool:
pr_err("ERR_MDMA:setup_dma failed: %d\n", err);
return err;
@@ -1219,7 +1232,7 @@ static void middma_shutdown(struct pci_dev *pdev)
* @pdev: Controller PCI device structure
* @id: pci device id structure
*
- * Initilize the PCI device, map BARs, query driver data.
+ * Initialize the PCI device, map BARs, query driver data.
* Call setup_dma to complete contoller and chan initilzation
*/
static int __devinit intel_mid_dma_probe(struct pci_dev *pdev,
@@ -1413,7 +1426,7 @@ static const struct dev_pm_ops intel_mid_dma_pm = {
.runtime_idle = dma_runtime_idle,
};
-static struct pci_driver intel_mid_dma_pci = {
+static struct pci_driver intel_mid_dma_pci_driver = {
.name = "Intel MID DMA",
.id_table = intel_mid_dma_ids,
.probe = intel_mid_dma_probe,
@@ -1431,13 +1444,13 @@ static int __init intel_mid_dma_init(void)
{
pr_debug("INFO_MDMA: LNW DMA Driver Version %s\n",
INTEL_MID_DMA_DRIVER_VERSION);
- return pci_register_driver(&intel_mid_dma_pci);
+ return pci_register_driver(&intel_mid_dma_pci_driver);
}
fs_initcall(intel_mid_dma_init);
static void __exit intel_mid_dma_exit(void)
{
- pci_unregister_driver(&intel_mid_dma_pci);
+ pci_unregister_driver(&intel_mid_dma_pci_driver);
}
module_exit(intel_mid_dma_exit);
diff --git a/drivers/dma/ioat/Makefile b/drivers/dma/ioat/Makefile
index 8997d3fb9051..0ff7270af25b 100644
--- a/drivers/dma/ioat/Makefile
+++ b/drivers/dma/ioat/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o
-ioatdma-objs := pci.o dma.o dma_v2.o dma_v3.o dca.o
+ioatdma-y := pci.o dma.o dma_v2.o dma_v3.o dca.o
diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c
index 161c452923b8..c6b01f535b29 100644
--- a/drivers/dma/iop-adma.c
+++ b/drivers/dma/iop-adma.c
@@ -1261,7 +1261,7 @@ out:
return err;
}
-#ifdef CONFIG_MD_RAID6_PQ
+#ifdef CONFIG_RAID6_PQ
static int __devinit
iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device)
{
@@ -1584,7 +1584,7 @@ static int __devinit iop_adma_probe(struct platform_device *pdev)
if (dma_has_cap(DMA_PQ, dma_dev->cap_mask) &&
dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask)) {
- #ifdef CONFIG_MD_RAID6_PQ
+ #ifdef CONFIG_RAID6_PQ
ret = iop_adma_pq_zero_sum_self_test(adev);
dev_dbg(&pdev->dev, "pq self test returned %d\n", ret);
#else
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index cb26ee9773d6..c1a125e7d1df 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -1145,29 +1145,6 @@ static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan,
reg = idmac_read_icreg(ipu, IDMAC_CHA_EN);
idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN);
- /*
- * Problem (observed with channel DMAIC_7): after enabling the channel
- * and initialising buffers, there comes an interrupt with current still
- * pointing at buffer 0, whereas it should use buffer 0 first and only
- * generate an interrupt when it is done, then current should already
- * point to buffer 1. This spurious interrupt also comes on channel
- * DMASDC_0. With DMAIC_7 normally, is we just leave the ISR after the
- * first interrupt, there comes the second with current correctly
- * pointing to buffer 1 this time. But sometimes this second interrupt
- * doesn't come and the channel hangs. Clearing BUFx_RDY when disabling
- * the channel seems to prevent the channel from hanging, but it doesn't
- * prevent the spurious interrupt. This might also be unsafe. Think
- * about the IDMAC controller trying to switch to a buffer, when we
- * clear the ready bit, and re-enable it a moment later.
- */
- reg = idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY);
- idmac_write_ipureg(ipu, 0, IPU_CHA_BUF0_RDY);
- idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF0_RDY);
-
- reg = idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY);
- idmac_write_ipureg(ipu, 0, IPU_CHA_BUF1_RDY);
- idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF1_RDY);
-
spin_unlock_irqrestore(&ipu->lock, flags);
return 0;
@@ -1246,33 +1223,6 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
/* Other interrupts do not interfere with this channel */
spin_lock(&ichan->lock);
- if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 &&
- ((curbuf >> chan_id) & 1) == ichan->active_buffer &&
- !list_is_last(ichan->queue.next, &ichan->queue))) {
- int i = 100;
-
- /* This doesn't help. See comment in ipu_disable_channel() */
- while (--i) {
- curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF);
- if (((curbuf >> chan_id) & 1) != ichan->active_buffer)
- break;
- cpu_relax();
- }
-
- if (!i) {
- spin_unlock(&ichan->lock);
- dev_dbg(dev,
- "IRQ on active buffer on channel %x, active "
- "%d, ready %x, %x, current %x!\n", chan_id,
- ichan->active_buffer, ready0, ready1, curbuf);
- return IRQ_NONE;
- } else
- dev_dbg(dev,
- "Buffer deactivated on channel %x, active "
- "%d, ready %x, %x, current %x, rest %d!\n", chan_id,
- ichan->active_buffer, ready0, ready1, curbuf, i);
- }
-
if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) ||
(!ichan->active_buffer && (ready0 >> chan_id) & 1)
)) {
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index 4e9cbf300594..4f95d31f5a20 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
* Copyright (C) Semihalf 2009
+ * Copyright (C) Ilya Yanok, Emcraft Systems 2010
*
* Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
* (defines, structures and comments) was taken from MPC5121 DMA driver
@@ -70,6 +71,8 @@
#define MPC_DMA_DMAES_SBE (1 << 1)
#define MPC_DMA_DMAES_DBE (1 << 0)
+#define MPC_DMA_DMAGPOR_SNOOP_ENABLE (1 << 6)
+
#define MPC_DMA_TSIZE_1 0x00
#define MPC_DMA_TSIZE_2 0x01
#define MPC_DMA_TSIZE_4 0x02
@@ -104,7 +107,10 @@ struct __attribute__ ((__packed__)) mpc_dma_regs {
/* 0x30 */
u32 dmahrsh; /* DMA hw request status high(ch63~32) */
u32 dmahrsl; /* DMA hardware request status low(ch31~0) */
- u32 dmaihsa; /* DMA interrupt high select AXE(ch63~32) */
+ union {
+ u32 dmaihsa; /* DMA interrupt high select AXE(ch63~32) */
+ u32 dmagpor; /* (General purpose register on MPC8308) */
+ };
u32 dmailsa; /* DMA interrupt low select AXE(ch31~0) */
/* 0x40 ~ 0xff */
u32 reserve0[48]; /* Reserved */
@@ -195,7 +201,9 @@ struct mpc_dma {
struct mpc_dma_regs __iomem *regs;
struct mpc_dma_tcd __iomem *tcd;
int irq;
+ int irq2;
uint error_status;
+ int is_mpc8308;
/* Lock for error_status field in this structure */
spinlock_t error_status_lock;
@@ -252,11 +260,13 @@ static void mpc_dma_execute(struct mpc_dma_chan *mchan)
prev = mdesc;
}
- prev->tcd->start = 0;
prev->tcd->int_maj = 1;
/* Send first descriptor in chain into hardware */
memcpy_toio(&mdma->tcd[cid], first->tcd, sizeof(struct mpc_dma_tcd));
+
+ if (first != prev)
+ mdma->tcd[cid].e_sg = 1;
out_8(&mdma->regs->dmassrt, cid);
}
@@ -274,6 +284,9 @@ static void mpc_dma_irq_process(struct mpc_dma *mdma, u32 is, u32 es, int off)
spin_lock(&mchan->lock);
+ out_8(&mdma->regs->dmacint, ch + off);
+ out_8(&mdma->regs->dmacerr, ch + off);
+
/* Check error status */
if (es & (1 << ch))
list_for_each_entry(mdesc, &mchan->active, node)
@@ -302,36 +315,68 @@ static irqreturn_t mpc_dma_irq(int irq, void *data)
spin_unlock(&mdma->error_status_lock);
/* Handle interrupt on each channel */
- mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
+ if (mdma->dma.chancnt > 32) {
+ mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
in_be32(&mdma->regs->dmaerrh), 32);
+ }
mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmaintl),
in_be32(&mdma->regs->dmaerrl), 0);
- /* Ack interrupt on all channels */
- out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
- out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
- out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
- out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
-
/* Schedule tasklet */
tasklet_schedule(&mdma->tasklet);
return IRQ_HANDLED;
}
-/* DMA Tasklet */
-static void mpc_dma_tasklet(unsigned long data)
+/* proccess completed descriptors */
+static void mpc_dma_process_completed(struct mpc_dma *mdma)
{
- struct mpc_dma *mdma = (void *)data;
dma_cookie_t last_cookie = 0;
struct mpc_dma_chan *mchan;
struct mpc_dma_desc *mdesc;
struct dma_async_tx_descriptor *desc;
unsigned long flags;
LIST_HEAD(list);
- uint es;
int i;
+ for (i = 0; i < mdma->dma.chancnt; i++) {
+ mchan = &mdma->channels[i];
+
+ /* Get all completed descriptors */
+ spin_lock_irqsave(&mchan->lock, flags);
+ if (!list_empty(&mchan->completed))
+ list_splice_tail_init(&mchan->completed, &list);
+ spin_unlock_irqrestore(&mchan->lock, flags);
+
+ if (list_empty(&list))
+ continue;
+
+ /* Execute callbacks and run dependencies */
+ list_for_each_entry(mdesc, &list, node) {
+ desc = &mdesc->desc;
+
+ if (desc->callback)
+ desc->callback(desc->callback_param);
+
+ last_cookie = desc->cookie;
+ dma_run_dependencies(desc);
+ }
+
+ /* Free descriptors */
+ spin_lock_irqsave(&mchan->lock, flags);
+ list_splice_tail_init(&list, &mchan->free);
+ mchan->completed_cookie = last_cookie;
+ spin_unlock_irqrestore(&mchan->lock, flags);
+ }
+}
+
+/* DMA Tasklet */
+static void mpc_dma_tasklet(unsigned long data)
+{
+ struct mpc_dma *mdma = (void *)data;
+ unsigned long flags;
+ uint es;
+
spin_lock_irqsave(&mdma->error_status_lock, flags);
es = mdma->error_status;
mdma->error_status = 0;
@@ -370,35 +415,7 @@ static void mpc_dma_tasklet(unsigned long data)
dev_err(mdma->dma.dev, "- Destination Bus Error\n");
}
- for (i = 0; i < mdma->dma.chancnt; i++) {
- mchan = &mdma->channels[i];
-
- /* Get all completed descriptors */
- spin_lock_irqsave(&mchan->lock, flags);
- if (!list_empty(&mchan->completed))
- list_splice_tail_init(&mchan->completed, &list);
- spin_unlock_irqrestore(&mchan->lock, flags);
-
- if (list_empty(&list))
- continue;
-
- /* Execute callbacks and run dependencies */
- list_for_each_entry(mdesc, &list, node) {
- desc = &mdesc->desc;
-
- if (desc->callback)
- desc->callback(desc->callback_param);
-
- last_cookie = desc->cookie;
- dma_run_dependencies(desc);
- }
-
- /* Free descriptors */
- spin_lock_irqsave(&mchan->lock, flags);
- list_splice_tail_init(&list, &mchan->free);
- mchan->completed_cookie = last_cookie;
- spin_unlock_irqrestore(&mchan->lock, flags);
- }
+ mpc_dma_process_completed(mdma);
}
/* Submit descriptor to hardware */
@@ -563,6 +580,7 @@ static struct dma_async_tx_descriptor *
mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
size_t len, unsigned long flags)
{
+ struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
struct mpc_dma_desc *mdesc = NULL;
struct mpc_dma_tcd *tcd;
@@ -577,8 +595,11 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
}
spin_unlock_irqrestore(&mchan->lock, iflags);
- if (!mdesc)
+ if (!mdesc) {
+ /* try to free completed descriptors */
+ mpc_dma_process_completed(mdma);
return NULL;
+ }
mdesc->error = 0;
tcd = mdesc->tcd;
@@ -591,7 +612,8 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
tcd->dsize = MPC_DMA_TSIZE_32;
tcd->soff = 32;
tcd->doff = 32;
- } else if (IS_ALIGNED(src | dst | len, 16)) {
+ } else if (!mdma->is_mpc8308 && IS_ALIGNED(src | dst | len, 16)) {
+ /* MPC8308 doesn't support 16 byte transfers */
tcd->ssize = MPC_DMA_TSIZE_16;
tcd->dsize = MPC_DMA_TSIZE_16;
tcd->soff = 16;
@@ -627,8 +649,7 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
return &mdesc->desc;
}
-static int __devinit mpc_dma_probe(struct platform_device *op,
- const struct of_device_id *match)
+static int __devinit mpc_dma_probe(struct platform_device *op)
{
struct device_node *dn = op->dev.of_node;
struct device *dev = &op->dev;
@@ -651,6 +672,15 @@ static int __devinit mpc_dma_probe(struct platform_device *op,
return -EINVAL;
}
+ if (of_device_is_compatible(dn, "fsl,mpc8308-dma")) {
+ mdma->is_mpc8308 = 1;
+ mdma->irq2 = irq_of_parse_and_map(dn, 1);
+ if (mdma->irq2 == NO_IRQ) {
+ dev_err(dev, "Error mapping IRQ!\n");
+ return -EINVAL;
+ }
+ }
+
retval = of_address_to_resource(dn, 0, &res);
if (retval) {
dev_err(dev, "Error parsing memory region!\n");
@@ -681,11 +711,23 @@ static int __devinit mpc_dma_probe(struct platform_device *op,
return -EINVAL;
}
+ if (mdma->is_mpc8308) {
+ retval = devm_request_irq(dev, mdma->irq2, &mpc_dma_irq, 0,
+ DRV_NAME, mdma);
+ if (retval) {
+ dev_err(dev, "Error requesting IRQ2!\n");
+ return -EINVAL;
+ }
+ }
+
spin_lock_init(&mdma->error_status_lock);
dma = &mdma->dma;
dma->dev = dev;
- dma->chancnt = MPC_DMA_CHANNELS;
+ if (!mdma->is_mpc8308)
+ dma->chancnt = MPC_DMA_CHANNELS;
+ else
+ dma->chancnt = 16; /* MPC8308 DMA has only 16 channels */
dma->device_alloc_chan_resources = mpc_dma_alloc_chan_resources;
dma->device_free_chan_resources = mpc_dma_free_chan_resources;
dma->device_issue_pending = mpc_dma_issue_pending;
@@ -721,26 +763,40 @@ static int __devinit mpc_dma_probe(struct platform_device *op,
* - Round-robin group arbitration,
* - Round-robin channel arbitration.
*/
- out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
- MPC_DMA_DMACR_ERGA | MPC_DMA_DMACR_ERCA);
-
- /* Disable hardware DMA requests */
- out_be32(&mdma->regs->dmaerqh, 0);
- out_be32(&mdma->regs->dmaerql, 0);
-
- /* Disable error interrupts */
- out_be32(&mdma->regs->dmaeeih, 0);
- out_be32(&mdma->regs->dmaeeil, 0);
+ if (!mdma->is_mpc8308) {
+ out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
+ MPC_DMA_DMACR_ERGA | MPC_DMA_DMACR_ERCA);
+
+ /* Disable hardware DMA requests */
+ out_be32(&mdma->regs->dmaerqh, 0);
+ out_be32(&mdma->regs->dmaerql, 0);
+
+ /* Disable error interrupts */
+ out_be32(&mdma->regs->dmaeeih, 0);
+ out_be32(&mdma->regs->dmaeeil, 0);
+
+ /* Clear interrupts status */
+ out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
+ out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
+ out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
+ out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
+
+ /* Route interrupts to IPIC */
+ out_be32(&mdma->regs->dmaihsa, 0);
+ out_be32(&mdma->regs->dmailsa, 0);
+ } else {
+ /* MPC8308 has 16 channels and lacks some registers */
+ out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_ERCA);
- /* Clear interrupts status */
- out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
- out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
- out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
- out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
+ /* enable snooping */
+ out_be32(&mdma->regs->dmagpor, MPC_DMA_DMAGPOR_SNOOP_ENABLE);
+ /* Disable error interrupts */
+ out_be32(&mdma->regs->dmaeeil, 0);
- /* Route interrupts to IPIC */
- out_be32(&mdma->regs->dmaihsa, 0);
- out_be32(&mdma->regs->dmailsa, 0);
+ /* Clear interrupts status */
+ out_be32(&mdma->regs->dmaintl, 0xFFFF);
+ out_be32(&mdma->regs->dmaerrl, 0xFFFF);
+ }
/* Register DMA engine */
dev_set_drvdata(dev, mdma);
@@ -770,7 +826,7 @@ static struct of_device_id mpc_dma_match[] = {
{},
};
-static struct of_platform_driver mpc_dma_driver = {
+static struct platform_driver mpc_dma_driver = {
.probe = mpc_dma_probe,
.remove = __devexit_p(mpc_dma_remove),
.driver = {
@@ -782,13 +838,13 @@ static struct of_platform_driver mpc_dma_driver = {
static int __init mpc_dma_init(void)
{
- return of_register_platform_driver(&mpc_dma_driver);
+ return platform_driver_register(&mpc_dma_driver);
}
module_init(mpc_dma_init);
static void __exit mpc_dma_exit(void)
{
- of_unregister_platform_driver(&mpc_dma_driver);
+ platform_driver_unregister(&mpc_dma_driver);
}
module_exit(mpc_dma_exit);
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 411d5bf50fc4..a25f5f61e0e0 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -449,7 +449,7 @@ mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
static void mv_xor_tasklet(unsigned long data)
{
struct mv_xor_chan *chan = (struct mv_xor_chan *) data;
- __mv_xor_slot_cleanup(chan);
+ mv_xor_slot_cleanup(chan);
}
static struct mv_xor_desc_slot *
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index 92b679024fed..1c38418ae61f 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -1,6 +1,7 @@
/*
* Topcliff PCH DMA controller driver
* Copyright (c) 2010 Intel Corporation
+ * Copyright (C) 2011 OKI SEMICONDUCTOR CO., LTD.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -259,11 +260,6 @@ static void pdc_dostart(struct pch_dma_chan *pd_chan, struct pch_dma_desc* desc)
return;
}
- channel_writel(pd_chan, DEV_ADDR, desc->regs.dev_addr);
- channel_writel(pd_chan, MEM_ADDR, desc->regs.mem_addr);
- channel_writel(pd_chan, SIZE, desc->regs.size);
- channel_writel(pd_chan, NEXT, desc->regs.next);
-
dev_dbg(chan2dev(&pd_chan->chan), "chan %d -> dev_addr: %x\n",
pd_chan->chan.chan_id, desc->regs.dev_addr);
dev_dbg(chan2dev(&pd_chan->chan), "chan %d -> mem_addr: %x\n",
@@ -273,10 +269,16 @@ static void pdc_dostart(struct pch_dma_chan *pd_chan, struct pch_dma_desc* desc)
dev_dbg(chan2dev(&pd_chan->chan), "chan %d -> next: %x\n",
pd_chan->chan.chan_id, desc->regs.next);
- if (list_empty(&desc->tx_list))
+ if (list_empty(&desc->tx_list)) {
+ channel_writel(pd_chan, DEV_ADDR, desc->regs.dev_addr);
+ channel_writel(pd_chan, MEM_ADDR, desc->regs.mem_addr);
+ channel_writel(pd_chan, SIZE, desc->regs.size);
+ channel_writel(pd_chan, NEXT, desc->regs.next);
pdc_set_mode(&pd_chan->chan, DMA_CTL0_ONESHOT);
- else
+ } else {
+ channel_writel(pd_chan, NEXT, desc->txd.phys);
pdc_set_mode(&pd_chan->chan, DMA_CTL0_SG);
+ }
val = dma_readl(pd, CTL2);
val |= 1 << (DMA_CTL2_START_SHIFT_BITS + pd_chan->chan.chan_id);
@@ -920,12 +922,19 @@ static void __devexit pch_dma_remove(struct pci_dev *pdev)
}
/* PCI Device ID of DMA device */
-#define PCI_DEVICE_ID_PCH_DMA_8CH 0x8810
-#define PCI_DEVICE_ID_PCH_DMA_4CH 0x8815
+#define PCI_VENDOR_ID_ROHM 0x10DB
+#define PCI_DEVICE_ID_EG20T_PCH_DMA_8CH 0x8810
+#define PCI_DEVICE_ID_EG20T_PCH_DMA_4CH 0x8815
+#define PCI_DEVICE_ID_ML7213_DMA1_8CH 0x8026
+#define PCI_DEVICE_ID_ML7213_DMA2_8CH 0x802B
+#define PCI_DEVICE_ID_ML7213_DMA3_4CH 0x8034
static const struct pci_device_id pch_dma_id_table[] = {
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_DMA_8CH), 8 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_DMA_4CH), 4 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_8CH), 8 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_4CH), 4 },
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA1_8CH), 8}, /* UART Video */
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA2_8CH), 8}, /* PCMIF SPI */
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA3_4CH), 4}, /* FPGA */
{ 0, },
};
@@ -953,6 +962,7 @@ static void __exit pch_dma_exit(void)
module_init(pch_dma_init);
module_exit(pch_dma_exit);
-MODULE_DESCRIPTION("Topcliff PCH DMA controller driver");
+MODULE_DESCRIPTION("Intel EG20T PCH / OKI SEMICONDUCTOR ML7213 IOH "
+ "DMA controller driver");
MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 7c50f6dfd3f4..6abe1ec1f2ce 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -657,7 +657,7 @@ static irqreturn_t pl330_irq_handler(int irq, void *data)
}
static int __devinit
-pl330_probe(struct amba_device *adev, struct amba_id *id)
+pl330_probe(struct amba_device *adev, const struct amba_id *id)
{
struct dma_pl330_platdata *pdat;
struct dma_pl330_dmac *pdmac;
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index 0d58a4a4487f..3b0247e74cc4 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -4393,8 +4393,7 @@ static void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
/**
* ppc440spe_adma_probe - probe the asynch device
*/
-static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
- const struct of_device_id *match)
+static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev)
{
struct device_node *np = ofdev->dev.of_node;
struct resource res;
@@ -4449,9 +4448,8 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
if (!request_mem_region(res.start, resource_size(&res),
dev_driver_string(&ofdev->dev))) {
- dev_err(&ofdev->dev, "failed to request memory region "
- "(0x%016llx-0x%016llx)\n",
- (u64)res.start, (u64)res.end);
+ dev_err(&ofdev->dev, "failed to request memory region %pR\n",
+ &res);
initcode = PPC_ADMA_INIT_MEMREG;
ret = -EBUSY;
goto out;
@@ -4945,7 +4943,7 @@ static const struct of_device_id ppc440spe_adma_of_match[] __devinitconst = {
};
MODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match);
-static struct of_platform_driver ppc440spe_adma_driver = {
+static struct platform_driver ppc440spe_adma_driver = {
.probe = ppc440spe_adma_probe,
.remove = __devexit_p(ppc440spe_adma_remove),
.driver = {
@@ -4963,7 +4961,7 @@ static __init int ppc440spe_adma_init(void)
if (ret)
return ret;
- ret = of_register_platform_driver(&ppc440spe_adma_driver);
+ ret = platform_driver_register(&ppc440spe_adma_driver);
if (ret) {
pr_err("%s: failed to register platform driver\n",
__func__);
@@ -4997,7 +4995,7 @@ out_dev:
/* User will not be able to enable h/w RAID-6 */
pr_err("%s: failed to create RAID-6 driver interface\n",
__func__);
- of_unregister_platform_driver(&ppc440spe_adma_driver);
+ platform_driver_unregister(&ppc440spe_adma_driver);
out_reg:
dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
kfree(ppc440spe_dma_fifo_buf);
@@ -5012,7 +5010,7 @@ static void __exit ppc440spe_adma_exit(void)
&driver_attr_enable);
driver_remove_file(&ppc440spe_adma_driver.driver,
&driver_attr_devices);
- of_unregister_platform_driver(&ppc440spe_adma_driver);
+ platform_driver_unregister(&ppc440spe_adma_driver);
dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
kfree(ppc440spe_dma_fifo_buf);
}
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 85ffd5e38c50..6451b581a70b 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -27,7 +27,10 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/sh_dma.h>
-
+#include <linux/notifier.h>
+#include <linux/kdebug.h>
+#include <linux/spinlock.h>
+#include <linux/rculist.h>
#include "shdma.h"
/* DMA descriptor control */
@@ -43,6 +46,13 @@ enum sh_dmae_desc_status {
/* Default MEMCPY transfer size = 2^2 = 4 bytes */
#define LOG2_DEFAULT_XFER_SIZE 2
+/*
+ * Used for write-side mutual exclusion for the global device list,
+ * read-side synchronization by way of RCU.
+ */
+static DEFINE_SPINLOCK(sh_dmae_lock);
+static LIST_HEAD(sh_dmae_devices);
+
/* A bitmask with bits enough for enum sh_dmae_slave_chan_id */
static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)];
@@ -740,7 +750,7 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
return;
}
- /* Find the first not transferred desciptor */
+ /* Find the first not transferred descriptor */
list_for_each_entry(desc, &sh_chan->ld_queue, node)
if (desc->mark == DESC_SUBMITTED) {
dev_dbg(sh_chan->dev, "Queue #%d to %d: %u@%x -> %x\n",
@@ -817,10 +827,9 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data)
return ret;
}
-#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
-static irqreturn_t sh_dmae_err(int irq, void *data)
+static unsigned int sh_dmae_reset(struct sh_dmae_device *shdev)
{
- struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
+ unsigned int handled = 0;
int i;
/* halt the dma controller */
@@ -829,25 +838,35 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
/* We cannot detect, which channel caused the error, have to reset all */
for (i = 0; i < SH_DMAC_MAX_CHANNELS; i++) {
struct sh_dmae_chan *sh_chan = shdev->chan[i];
- if (sh_chan) {
- struct sh_desc *desc;
- /* Stop the channel */
- dmae_halt(sh_chan);
- /* Complete all */
- list_for_each_entry(desc, &sh_chan->ld_queue, node) {
- struct dma_async_tx_descriptor *tx = &desc->async_tx;
- desc->mark = DESC_IDLE;
- if (tx->callback)
- tx->callback(tx->callback_param);
- }
- list_splice_init(&sh_chan->ld_queue, &sh_chan->ld_free);
+ struct sh_desc *desc;
+
+ if (!sh_chan)
+ continue;
+
+ /* Stop the channel */
+ dmae_halt(sh_chan);
+
+ /* Complete all */
+ list_for_each_entry(desc, &sh_chan->ld_queue, node) {
+ struct dma_async_tx_descriptor *tx = &desc->async_tx;
+ desc->mark = DESC_IDLE;
+ if (tx->callback)
+ tx->callback(tx->callback_param);
}
+
+ list_splice_init(&sh_chan->ld_queue, &sh_chan->ld_free);
+ handled++;
}
+
sh_dmae_rst(shdev);
- return IRQ_HANDLED;
+ return !!handled;
+}
+
+static irqreturn_t sh_dmae_err(int irq, void *data)
+{
+ return IRQ_RETVAL(sh_dmae_reset(data));
}
-#endif
static void dmae_do_tasklet(unsigned long data)
{
@@ -876,6 +895,60 @@ static void dmae_do_tasklet(unsigned long data)
sh_dmae_chan_ld_cleanup(sh_chan, false);
}
+static bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev)
+{
+ unsigned int handled;
+
+ /* Fast path out if NMIF is not asserted for this controller */
+ if ((dmaor_read(shdev) & DMAOR_NMIF) == 0)
+ return false;
+
+ handled = sh_dmae_reset(shdev);
+ if (handled)
+ return true;
+
+ return false;
+}
+
+static int sh_dmae_nmi_handler(struct notifier_block *self,
+ unsigned long cmd, void *data)
+{
+ struct sh_dmae_device *shdev;
+ int ret = NOTIFY_DONE;
+ bool triggered;
+
+ /*
+ * Only concern ourselves with NMI events.
+ *
+ * Normally we would check the die chain value, but as this needs
+ * to be architecture independent, check for NMI context instead.
+ */
+ if (!in_nmi())
+ return NOTIFY_DONE;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(shdev, &sh_dmae_devices, node) {
+ /*
+ * Only stop if one of the controllers has NMIF asserted,
+ * we do not want to interfere with regular address error
+ * handling or NMI events that don't concern the DMACs.
+ */
+ triggered = sh_dmae_nmi_notify(shdev);
+ if (triggered == true)
+ ret = NOTIFY_OK;
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static struct notifier_block sh_dmae_nmi_notifier __read_mostly = {
+ .notifier_call = sh_dmae_nmi_handler,
+
+ /* Run before NMI debug handler and KGDB */
+ .priority = 1,
+};
+
static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
int irq, unsigned long flags)
{
@@ -967,6 +1040,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
struct sh_dmae_pdata *pdata = pdev->dev.platform_data;
unsigned long irqflags = IRQF_DISABLED,
chan_flag[SH_DMAC_MAX_CHANNELS] = {};
+ unsigned long flags;
int errirq, chan_irq[SH_DMAC_MAX_CHANNELS];
int err, i, irq_cnt = 0, irqres = 0;
struct sh_dmae_device *shdev;
@@ -1032,6 +1106,10 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
+ spin_lock_irqsave(&sh_dmae_lock, flags);
+ list_add_tail_rcu(&shdev->node, &sh_dmae_devices);
+ spin_unlock_irqrestore(&sh_dmae_lock, flags);
+
/* reset dma controller */
err = sh_dmae_rst(shdev);
if (err)
@@ -1135,6 +1213,10 @@ eirqres:
eirq_err:
#endif
rst_err:
+ spin_lock_irqsave(&sh_dmae_lock, flags);
+ list_del_rcu(&shdev->node);
+ spin_unlock_irqrestore(&sh_dmae_lock, flags);
+
pm_runtime_put(&pdev->dev);
if (dmars)
iounmap(shdev->dmars);
@@ -1155,6 +1237,7 @@ static int __exit sh_dmae_remove(struct platform_device *pdev)
{
struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
struct resource *res;
+ unsigned long flags;
int errirq = platform_get_irq(pdev, 0);
dma_async_device_unregister(&shdev->common);
@@ -1162,6 +1245,10 @@ static int __exit sh_dmae_remove(struct platform_device *pdev)
if (errirq > 0)
free_irq(errirq, shdev);
+ spin_lock_irqsave(&sh_dmae_lock, flags);
+ list_del_rcu(&shdev->node);
+ spin_unlock_irqrestore(&sh_dmae_lock, flags);
+
/* channel data remove */
sh_dmae_chan_remove(shdev);
@@ -1200,6 +1287,11 @@ static struct platform_driver sh_dmae_driver = {
static int __init sh_dmae_init(void)
{
+ /* Wire up NMI handling */
+ int err = register_die_notifier(&sh_dmae_nmi_notifier);
+ if (err)
+ return err;
+
return platform_driver_probe(&sh_dmae_driver, sh_dmae_probe);
}
module_init(sh_dmae_init);
@@ -1207,6 +1299,8 @@ module_init(sh_dmae_init);
static void __exit sh_dmae_exit(void)
{
platform_driver_unregister(&sh_dmae_driver);
+
+ unregister_die_notifier(&sh_dmae_nmi_notifier);
}
module_exit(sh_dmae_exit);
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 4021275a0a43..52e4fb173805 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -43,6 +43,7 @@ struct sh_dmae_device {
struct dma_device common;
struct sh_dmae_chan *chan[SH_DMAC_MAX_CHANNELS];
struct sh_dmae_pdata *pdata;
+ struct list_head node;
u32 __iomem *chan_reg;
u16 __iomem *dmars;
};
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index fab68a553205..6e1d46a65d0e 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -1,5 +1,6 @@
/*
- * Copyright (C) ST-Ericsson SA 2007-2010
+ * Copyright (C) Ericsson AB 2007-2008
+ * Copyright (C) ST-Ericsson SA 2008-2010
* Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
* Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
@@ -554,8 +555,66 @@ static struct d40_desc *d40_last_queued(struct d40_chan *d40c)
return d;
}
-/* Support functions for logical channels */
+static int d40_psize_2_burst_size(bool is_log, int psize)
+{
+ if (is_log) {
+ if (psize == STEDMA40_PSIZE_LOG_1)
+ return 1;
+ } else {
+ if (psize == STEDMA40_PSIZE_PHY_1)
+ return 1;
+ }
+
+ return 2 << psize;
+}
+
+/*
+ * The dma only supports transmitting packages up to
+ * STEDMA40_MAX_SEG_SIZE << data_width. Calculate the total number of
+ * dma elements required to send the entire sg list
+ */
+static int d40_size_2_dmalen(int size, u32 data_width1, u32 data_width2)
+{
+ int dmalen;
+ u32 max_w = max(data_width1, data_width2);
+ u32 min_w = min(data_width1, data_width2);
+ u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w);
+
+ if (seg_max > STEDMA40_MAX_SEG_SIZE)
+ seg_max -= (1 << max_w);
+
+ if (!IS_ALIGNED(size, 1 << max_w))
+ return -EINVAL;
+
+ if (size <= seg_max)
+ dmalen = 1;
+ else {
+ dmalen = size / seg_max;
+ if (dmalen * seg_max < size)
+ dmalen++;
+ }
+ return dmalen;
+}
+
+static int d40_sg_2_dmalen(struct scatterlist *sgl, int sg_len,
+ u32 data_width1, u32 data_width2)
+{
+ struct scatterlist *sg;
+ int i;
+ int len = 0;
+ int ret;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ ret = d40_size_2_dmalen(sg_dma_len(sg),
+ data_width1, data_width2);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }
+ return len;
+}
+/* Support functions for logical channels */
static int d40_channel_execute_command(struct d40_chan *d40c,
enum d40_command command)
@@ -1241,6 +1300,21 @@ static int d40_validate_conf(struct d40_chan *d40c,
res = -EINVAL;
}
+ if (d40_psize_2_burst_size(is_log, conf->src_info.psize) *
+ (1 << conf->src_info.data_width) !=
+ d40_psize_2_burst_size(is_log, conf->dst_info.psize) *
+ (1 << conf->dst_info.data_width)) {
+ /*
+ * The DMAC hardware only supports
+ * src (burst x width) == dst (burst x width)
+ */
+
+ dev_err(&d40c->chan.dev->device,
+ "[%s] src (burst x width) != dst (burst x width)\n",
+ __func__);
+ res = -EINVAL;
+ }
+
return res;
}
@@ -1638,13 +1712,21 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
if (d40d == NULL)
goto err;
- d40d->lli_len = sgl_len;
+ d40d->lli_len = d40_sg_2_dmalen(sgl_dst, sgl_len,
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width);
+ if (d40d->lli_len < 0) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Unaligned size\n", __func__);
+ goto err;
+ }
+
d40d->lli_current = 0;
d40d->txd.flags = dma_flags;
if (d40c->log_num != D40_PHY_CHAN) {
- if (d40_pool_lli_alloc(d40d, sgl_len, true) < 0) {
+ if (d40_pool_lli_alloc(d40d, d40d->lli_len, true) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
goto err;
@@ -1654,15 +1736,17 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
sgl_len,
d40d->lli_log.src,
d40c->log_def.lcsp1,
- d40c->dma_cfg.src_info.data_width);
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width);
(void) d40_log_sg_to_lli(sgl_dst,
sgl_len,
d40d->lli_log.dst,
d40c->log_def.lcsp3,
- d40c->dma_cfg.dst_info.data_width);
+ d40c->dma_cfg.dst_info.data_width,
+ d40c->dma_cfg.src_info.data_width);
} else {
- if (d40_pool_lli_alloc(d40d, sgl_len, false) < 0) {
+ if (d40_pool_lli_alloc(d40d, d40d->lli_len, false) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
goto err;
@@ -1675,6 +1759,7 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
virt_to_phys(d40d->lli_phy.src),
d40c->src_def_cfg,
d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width,
d40c->dma_cfg.src_info.psize);
if (res < 0)
@@ -1687,6 +1772,7 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
virt_to_phys(d40d->lli_phy.dst),
d40c->dst_def_cfg,
d40c->dma_cfg.dst_info.data_width,
+ d40c->dma_cfg.src_info.data_width,
d40c->dma_cfg.dst_info.psize);
if (res < 0)
@@ -1826,7 +1912,6 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
struct d40_chan *d40c = container_of(chan, struct d40_chan,
chan);
unsigned long flags;
- int err = 0;
if (d40c->phy_chan == NULL) {
dev_err(&d40c->chan.dev->device,
@@ -1844,6 +1929,15 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
}
d40d->txd.flags = dma_flags;
+ d40d->lli_len = d40_size_2_dmalen(size,
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width);
+ if (d40d->lli_len < 0) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Unaligned size\n", __func__);
+ goto err;
+ }
+
dma_async_tx_descriptor_init(&d40d->txd, chan);
@@ -1851,37 +1945,40 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
if (d40c->log_num != D40_PHY_CHAN) {
- if (d40_pool_lli_alloc(d40d, 1, true) < 0) {
+ if (d40_pool_lli_alloc(d40d, d40d->lli_len, true) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
goto err;
}
- d40d->lli_len = 1;
d40d->lli_current = 0;
- d40_log_fill_lli(d40d->lli_log.src,
- src,
- size,
- d40c->log_def.lcsp1,
- d40c->dma_cfg.src_info.data_width,
- true);
+ if (d40_log_buf_to_lli(d40d->lli_log.src,
+ src,
+ size,
+ d40c->log_def.lcsp1,
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width,
+ true) == NULL)
+ goto err;
- d40_log_fill_lli(d40d->lli_log.dst,
- dst,
- size,
- d40c->log_def.lcsp3,
- d40c->dma_cfg.dst_info.data_width,
- true);
+ if (d40_log_buf_to_lli(d40d->lli_log.dst,
+ dst,
+ size,
+ d40c->log_def.lcsp3,
+ d40c->dma_cfg.dst_info.data_width,
+ d40c->dma_cfg.src_info.data_width,
+ true) == NULL)
+ goto err;
} else {
- if (d40_pool_lli_alloc(d40d, 1, false) < 0) {
+ if (d40_pool_lli_alloc(d40d, d40d->lli_len, false) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
goto err;
}
- err = d40_phy_fill_lli(d40d->lli_phy.src,
+ if (d40_phy_buf_to_lli(d40d->lli_phy.src,
src,
size,
d40c->dma_cfg.src_info.psize,
@@ -1889,11 +1986,11 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
d40c->src_def_cfg,
true,
d40c->dma_cfg.src_info.data_width,
- false);
- if (err)
- goto err_fill_lli;
+ d40c->dma_cfg.dst_info.data_width,
+ false) == NULL)
+ goto err;
- err = d40_phy_fill_lli(d40d->lli_phy.dst,
+ if (d40_phy_buf_to_lli(d40d->lli_phy.dst,
dst,
size,
d40c->dma_cfg.dst_info.psize,
@@ -1901,10 +1998,9 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
d40c->dst_def_cfg,
true,
d40c->dma_cfg.dst_info.data_width,
- false);
-
- if (err)
- goto err_fill_lli;
+ d40c->dma_cfg.src_info.data_width,
+ false) == NULL)
+ goto err;
(void) dma_map_single(d40c->base->dev, d40d->lli_phy.src,
d40d->lli_pool.size, DMA_TO_DEVICE);
@@ -1913,9 +2009,6 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
spin_unlock_irqrestore(&d40c->lock, flags);
return &d40d->txd;
-err_fill_lli:
- dev_err(&d40c->chan.dev->device,
- "[%s] Failed filling in PHY LLI\n", __func__);
err:
if (d40d)
d40_desc_free(d40c, d40d);
@@ -1945,13 +2038,21 @@ static int d40_prep_slave_sg_log(struct d40_desc *d40d,
dma_addr_t dev_addr = 0;
int total_size;
- if (d40_pool_lli_alloc(d40d, sg_len, true) < 0) {
+ d40d->lli_len = d40_sg_2_dmalen(sgl, sg_len,
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width);
+ if (d40d->lli_len < 0) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Unaligned size\n", __func__);
+ return -EINVAL;
+ }
+
+ if (d40_pool_lli_alloc(d40d, d40d->lli_len, true) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
return -ENOMEM;
}
- d40d->lli_len = sg_len;
d40d->lli_current = 0;
if (direction == DMA_FROM_DEVICE)
@@ -1993,13 +2094,21 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
dma_addr_t dst_dev_addr;
int res;
- if (d40_pool_lli_alloc(d40d, sgl_len, false) < 0) {
+ d40d->lli_len = d40_sg_2_dmalen(sgl, sgl_len,
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width);
+ if (d40d->lli_len < 0) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Unaligned size\n", __func__);
+ return -EINVAL;
+ }
+
+ if (d40_pool_lli_alloc(d40d, d40d->lli_len, false) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
return -ENOMEM;
}
- d40d->lli_len = sgl_len;
d40d->lli_current = 0;
if (direction == DMA_FROM_DEVICE) {
@@ -2024,6 +2133,7 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
virt_to_phys(d40d->lli_phy.src),
d40c->src_def_cfg,
d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width,
d40c->dma_cfg.src_info.psize);
if (res < 0)
return res;
@@ -2035,6 +2145,7 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
virt_to_phys(d40d->lli_phy.dst),
d40c->dst_def_cfg,
d40c->dma_cfg.dst_info.data_width,
+ d40c->dma_cfg.src_info.data_width,
d40c->dma_cfg.dst_info.psize);
if (res < 0)
return res;
@@ -2244,6 +2355,8 @@ static void d40_set_runtime_config(struct dma_chan *chan,
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;
}
diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c
index 8557cb88b255..0b096a38322d 100644
--- a/drivers/dma/ste_dma40_ll.c
+++ b/drivers/dma/ste_dma40_ll.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson SA 2007-2010
- * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson
+ * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
* Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
*/
@@ -122,15 +122,15 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
*dst_cfg = dst;
}
-int d40_phy_fill_lli(struct d40_phy_lli *lli,
- dma_addr_t data,
- u32 data_size,
- int psize,
- dma_addr_t next_lli,
- u32 reg_cfg,
- bool term_int,
- u32 data_width,
- bool is_device)
+static int d40_phy_fill_lli(struct d40_phy_lli *lli,
+ dma_addr_t data,
+ u32 data_size,
+ int psize,
+ dma_addr_t next_lli,
+ u32 reg_cfg,
+ bool term_int,
+ u32 data_width,
+ bool is_device)
{
int num_elems;
@@ -139,13 +139,6 @@ int d40_phy_fill_lli(struct d40_phy_lli *lli,
else
num_elems = 2 << psize;
- /*
- * Size is 16bit. data_width is 8, 16, 32 or 64 bit
- * Block large than 64 KiB must be split.
- */
- if (data_size > (0xffff << data_width))
- return -EINVAL;
-
/* Must be aligned */
if (!IS_ALIGNED(data, 0x1 << data_width))
return -EINVAL;
@@ -187,55 +180,118 @@ int d40_phy_fill_lli(struct d40_phy_lli *lli,
return 0;
}
+static int d40_seg_size(int size, int data_width1, int data_width2)
+{
+ u32 max_w = max(data_width1, data_width2);
+ u32 min_w = min(data_width1, data_width2);
+ u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w);
+
+ if (seg_max > STEDMA40_MAX_SEG_SIZE)
+ seg_max -= (1 << max_w);
+
+ if (size <= seg_max)
+ return size;
+
+ if (size <= 2 * seg_max)
+ return ALIGN(size / 2, 1 << max_w);
+
+ return seg_max;
+}
+
+struct d40_phy_lli *d40_phy_buf_to_lli(struct d40_phy_lli *lli,
+ dma_addr_t addr,
+ u32 size,
+ int psize,
+ dma_addr_t lli_phys,
+ u32 reg_cfg,
+ bool term_int,
+ u32 data_width1,
+ u32 data_width2,
+ bool is_device)
+{
+ int err;
+ dma_addr_t next = lli_phys;
+ int size_rest = size;
+ int size_seg = 0;
+
+ do {
+ size_seg = d40_seg_size(size_rest, data_width1, data_width2);
+ size_rest -= size_seg;
+
+ if (term_int && size_rest == 0)
+ next = 0;
+ else
+ next = ALIGN(next + sizeof(struct d40_phy_lli),
+ D40_LLI_ALIGN);
+
+ err = d40_phy_fill_lli(lli,
+ addr,
+ size_seg,
+ psize,
+ next,
+ reg_cfg,
+ !next,
+ data_width1,
+ is_device);
+
+ if (err)
+ goto err;
+
+ lli++;
+ if (!is_device)
+ addr += size_seg;
+ } while (size_rest);
+
+ return lli;
+
+ err:
+ return NULL;
+}
+
int d40_phy_sg_to_lli(struct scatterlist *sg,
int sg_len,
dma_addr_t target,
- struct d40_phy_lli *lli,
+ struct d40_phy_lli *lli_sg,
dma_addr_t lli_phys,
u32 reg_cfg,
- u32 data_width,
+ u32 data_width1,
+ u32 data_width2,
int psize)
{
int total_size = 0;
int i;
struct scatterlist *current_sg = sg;
- dma_addr_t next_lli_phys;
dma_addr_t dst;
- int err = 0;
+ struct d40_phy_lli *lli = lli_sg;
+ dma_addr_t l_phys = lli_phys;
for_each_sg(sg, current_sg, sg_len, i) {
total_size += sg_dma_len(current_sg);
- /* If this scatter list entry is the last one, no next link */
- if (sg_len - 1 == i)
- next_lli_phys = 0;
- else
- next_lli_phys = ALIGN(lli_phys + (i + 1) *
- sizeof(struct d40_phy_lli),
- D40_LLI_ALIGN);
-
if (target)
dst = target;
else
dst = sg_phys(current_sg);
- err = d40_phy_fill_lli(&lli[i],
- dst,
- sg_dma_len(current_sg),
- psize,
- next_lli_phys,
- reg_cfg,
- !next_lli_phys,
- data_width,
- target == dst);
- if (err)
- goto err;
+ l_phys = ALIGN(lli_phys + (lli - lli_sg) *
+ sizeof(struct d40_phy_lli), D40_LLI_ALIGN);
+
+ lli = d40_phy_buf_to_lli(lli,
+ dst,
+ sg_dma_len(current_sg),
+ psize,
+ l_phys,
+ reg_cfg,
+ sg_len - 1 == i,
+ data_width1,
+ data_width2,
+ target == dst);
+ if (lli == NULL)
+ return -EINVAL;
}
return total_size;
-err:
- return err;
}
@@ -315,17 +371,20 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
writel(lli_dst->lcsp13, &lcla[1].lcsp13);
}
-void d40_log_fill_lli(struct d40_log_lli *lli,
- dma_addr_t data, u32 data_size,
- u32 reg_cfg,
- u32 data_width,
- bool addr_inc)
+static void d40_log_fill_lli(struct d40_log_lli *lli,
+ dma_addr_t data, u32 data_size,
+ u32 reg_cfg,
+ u32 data_width,
+ bool addr_inc)
{
lli->lcsp13 = reg_cfg;
/* The number of elements to transfer */
lli->lcsp02 = ((data_size >> data_width) <<
D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK;
+
+ BUG_ON((data_size >> data_width) > STEDMA40_MAX_SEG_SIZE);
+
/* 16 LSBs address of the current element */
lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK;
/* 16 MSBs address of the current element */
@@ -348,55 +407,94 @@ int d40_log_sg_to_dev(struct scatterlist *sg,
int total_size = 0;
struct scatterlist *current_sg = sg;
int i;
+ struct d40_log_lli *lli_src = lli->src;
+ struct d40_log_lli *lli_dst = lli->dst;
for_each_sg(sg, current_sg, sg_len, i) {
total_size += sg_dma_len(current_sg);
if (direction == DMA_TO_DEVICE) {
- d40_log_fill_lli(&lli->src[i],
- sg_phys(current_sg),
- sg_dma_len(current_sg),
- lcsp->lcsp1, src_data_width,
- true);
- d40_log_fill_lli(&lli->dst[i],
- dev_addr,
- sg_dma_len(current_sg),
- lcsp->lcsp3, dst_data_width,
- false);
+ lli_src =
+ d40_log_buf_to_lli(lli_src,
+ sg_phys(current_sg),
+ sg_dma_len(current_sg),
+ lcsp->lcsp1, src_data_width,
+ dst_data_width,
+ true);
+ lli_dst =
+ d40_log_buf_to_lli(lli_dst,
+ dev_addr,
+ sg_dma_len(current_sg),
+ lcsp->lcsp3, dst_data_width,
+ src_data_width,
+ false);
} else {
- d40_log_fill_lli(&lli->dst[i],
- sg_phys(current_sg),
- sg_dma_len(current_sg),
- lcsp->lcsp3, dst_data_width,
- true);
- d40_log_fill_lli(&lli->src[i],
- dev_addr,
- sg_dma_len(current_sg),
- lcsp->lcsp1, src_data_width,
- false);
+ lli_dst =
+ d40_log_buf_to_lli(lli_dst,
+ sg_phys(current_sg),
+ sg_dma_len(current_sg),
+ lcsp->lcsp3, dst_data_width,
+ src_data_width,
+ true);
+ lli_src =
+ d40_log_buf_to_lli(lli_src,
+ dev_addr,
+ sg_dma_len(current_sg),
+ lcsp->lcsp1, src_data_width,
+ dst_data_width,
+ false);
}
}
return total_size;
}
+struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
+ dma_addr_t addr,
+ int size,
+ u32 lcsp13, /* src or dst*/
+ u32 data_width1,
+ u32 data_width2,
+ bool addr_inc)
+{
+ struct d40_log_lli *lli = lli_sg;
+ int size_rest = size;
+ int size_seg = 0;
+
+ do {
+ size_seg = d40_seg_size(size_rest, data_width1, data_width2);
+ size_rest -= size_seg;
+
+ d40_log_fill_lli(lli,
+ addr,
+ size_seg,
+ lcsp13, data_width1,
+ addr_inc);
+ if (addr_inc)
+ addr += size_seg;
+ lli++;
+ } while (size_rest);
+
+ return lli;
+}
+
int d40_log_sg_to_lli(struct scatterlist *sg,
int sg_len,
struct d40_log_lli *lli_sg,
u32 lcsp13, /* src or dst*/
- u32 data_width)
+ u32 data_width1, u32 data_width2)
{
int total_size = 0;
struct scatterlist *current_sg = sg;
int i;
+ struct d40_log_lli *lli = lli_sg;
for_each_sg(sg, current_sg, sg_len, i) {
total_size += sg_dma_len(current_sg);
-
- d40_log_fill_lli(&lli_sg[i],
- sg_phys(current_sg),
- sg_dma_len(current_sg),
- lcsp13, data_width,
- true);
+ lli = d40_log_buf_to_lli(lli,
+ sg_phys(current_sg),
+ sg_dma_len(current_sg),
+ lcsp13,
+ data_width1, data_width2, true);
}
return total_size;
}
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 9e419b907544..9cc43495bea2 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -292,18 +292,20 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
struct d40_phy_lli *lli,
dma_addr_t lli_phys,
u32 reg_cfg,
- u32 data_width,
+ u32 data_width1,
+ u32 data_width2,
int psize);
-int d40_phy_fill_lli(struct d40_phy_lli *lli,
- dma_addr_t data,
- u32 data_size,
- int psize,
- dma_addr_t next_lli,
- u32 reg_cfg,
- bool term_int,
- u32 data_width,
- bool is_device);
+struct d40_phy_lli *d40_phy_buf_to_lli(struct d40_phy_lli *lli,
+ dma_addr_t data,
+ u32 data_size,
+ int psize,
+ dma_addr_t next_lli,
+ u32 reg_cfg,
+ bool term_int,
+ u32 data_width1,
+ u32 data_width2,
+ bool is_device);
void d40_phy_lli_write(void __iomem *virtbase,
u32 phy_chan_num,
@@ -312,12 +314,12 @@ void d40_phy_lli_write(void __iomem *virtbase,
/* Logical channels */
-void d40_log_fill_lli(struct d40_log_lli *lli,
- dma_addr_t data,
- u32 data_size,
- u32 reg_cfg,
- u32 data_width,
- bool addr_inc);
+struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg,
+ dma_addr_t addr,
+ int size,
+ u32 lcsp13, /* src or dst*/
+ u32 data_width1, u32 data_width2,
+ bool addr_inc);
int d40_log_sg_to_dev(struct scatterlist *sg,
int sg_len,
@@ -332,7 +334,7 @@ int d40_log_sg_to_lli(struct scatterlist *sg,
int sg_len,
struct d40_log_lli *lli_sg,
u32 lcsp13, /* src or dst*/
- u32 data_width);
+ u32 data_width1, u32 data_width2);
void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
struct d40_log_lli *lli_dst,
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index 3b88a4e7c98a..f69f90a61873 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -629,7 +629,7 @@ static int td_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
desc_node)
list_move(&td_desc->desc_node, &td_chan->free_list);
- /* now tear down the runnning */
+ /* now tear down the running */
__td_finish(td_chan);
spin_unlock_bh(&td_chan->lock);