Merge branch 'dmaengine' into async-tx-next
Conflicts: crypto/async_tx/async_xor.c drivers/dma/ioat/dma_v2.h drivers/dma/ioat/pci.c drivers/md/raid5.c
This commit is contained in:
@@ -50,6 +50,14 @@ config DW_DMAC
|
||||
Support the Synopsys DesignWare AHB DMA controller. This
|
||||
can be integrated in chips such as the Atmel AT32ap7000.
|
||||
|
||||
config AT_HDMAC
|
||||
tristate "Atmel AHB DMA support"
|
||||
depends on ARCH_AT91SAM9RL
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Support the Atmel AHB DMA controller. This can be integrated in
|
||||
chips such as the Atmel AT91SAM9RL.
|
||||
|
||||
config FSL_DMA
|
||||
tristate "Freescale Elo and Elo Plus DMA support"
|
||||
depends on FSL_SOC
|
||||
@@ -85,6 +93,14 @@ config MX3_IPU_IRQS
|
||||
To avoid bloating the irq_desc[] array we allocate a sufficient
|
||||
number of IRQ slots and map them dynamically to specific sources.
|
||||
|
||||
config TXX9_DMAC
|
||||
tristate "Toshiba TXx9 SoC DMA support"
|
||||
depends on MACH_TX49XX || MACH_TX39XX
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Support the TXx9 SoC internal DMA controller. This can be
|
||||
integrated in chips such as the Toshiba TX4927/38/39.
|
||||
|
||||
config DMA_ENGINE
|
||||
bool
|
||||
|
||||
@@ -104,7 +120,7 @@ config NET_DMA
|
||||
|
||||
config ASYNC_TX_DMA
|
||||
bool "Async_tx: Offload support for the async_tx api"
|
||||
depends on DMA_ENGINE
|
||||
depends on DMA_ENGINE && !HIGHMEM64G
|
||||
help
|
||||
This allows the async_tx api to take advantage of offload engines for
|
||||
memcpy, memset, xor, and raid6 p+q operations. If your platform has
|
||||
|
@@ -6,4 +6,6 @@ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o
|
||||
obj-$(CONFIG_FSL_DMA) += fsldma.o
|
||||
obj-$(CONFIG_MV_XOR) += mv_xor.o
|
||||
obj-$(CONFIG_DW_DMAC) += dw_dmac.o
|
||||
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
|
||||
obj-$(CONFIG_MX3_IPU) += ipu/
|
||||
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
|
||||
|
1215
drivers/dma/at_hdmac.c
Normal file
1215
drivers/dma/at_hdmac.c
Normal file
File diff suppressed because it is too large
Load Diff
354
drivers/dma/at_hdmac_regs.h
Normal file
354
drivers/dma/at_hdmac_regs.h
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Header file for the Atmel AHB DMA Controller driver
|
||||
*
|
||||
* Copyright (C) 2008 Atmel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
#ifndef AT_HDMAC_REGS_H
|
||||
#define AT_HDMAC_REGS_H
|
||||
|
||||
#include <mach/at_hdmac.h>
|
||||
|
||||
#define AT_DMA_MAX_NR_CHANNELS 8
|
||||
|
||||
|
||||
#define AT_DMA_GCFG 0x00 /* Global Configuration Register */
|
||||
#define AT_DMA_IF_BIGEND(i) (0x1 << (i)) /* AHB-Lite Interface i in Big-endian mode */
|
||||
#define AT_DMA_ARB_CFG (0x1 << 4) /* Arbiter mode. */
|
||||
#define AT_DMA_ARB_CFG_FIXED (0x0 << 4)
|
||||
#define AT_DMA_ARB_CFG_ROUND_ROBIN (0x1 << 4)
|
||||
|
||||
#define AT_DMA_EN 0x04 /* Controller Enable Register */
|
||||
#define AT_DMA_ENABLE (0x1 << 0)
|
||||
|
||||
#define AT_DMA_SREQ 0x08 /* Software Single Request Register */
|
||||
#define AT_DMA_SSREQ(x) (0x1 << ((x) << 1)) /* Request a source single transfer on channel x */
|
||||
#define AT_DMA_DSREQ(x) (0x1 << (1 + ((x) << 1))) /* Request a destination single transfer on channel x */
|
||||
|
||||
#define AT_DMA_CREQ 0x0C /* Software Chunk Transfer Request Register */
|
||||
#define AT_DMA_SCREQ(x) (0x1 << ((x) << 1)) /* Request a source chunk transfer on channel x */
|
||||
#define AT_DMA_DCREQ(x) (0x1 << (1 + ((x) << 1))) /* Request a destination chunk transfer on channel x */
|
||||
|
||||
#define AT_DMA_LAST 0x10 /* Software Last Transfer Flag Register */
|
||||
#define AT_DMA_SLAST(x) (0x1 << ((x) << 1)) /* This src rq is last tx of buffer on channel x */
|
||||
#define AT_DMA_DLAST(x) (0x1 << (1 + ((x) << 1))) /* This dst rq is last tx of buffer on channel x */
|
||||
|
||||
#define AT_DMA_SYNC 0x14 /* Request Synchronization Register */
|
||||
#define AT_DMA_SYR(h) (0x1 << (h)) /* Synchronize handshake line h */
|
||||
|
||||
/* Error, Chained Buffer transfer completed and Buffer transfer completed Interrupt registers */
|
||||
#define AT_DMA_EBCIER 0x18 /* Enable register */
|
||||
#define AT_DMA_EBCIDR 0x1C /* Disable register */
|
||||
#define AT_DMA_EBCIMR 0x20 /* Mask Register */
|
||||
#define AT_DMA_EBCISR 0x24 /* Status Register */
|
||||
#define AT_DMA_CBTC_OFFSET 8
|
||||
#define AT_DMA_ERR_OFFSET 16
|
||||
#define AT_DMA_BTC(x) (0x1 << (x))
|
||||
#define AT_DMA_CBTC(x) (0x1 << (AT_DMA_CBTC_OFFSET + (x)))
|
||||
#define AT_DMA_ERR(x) (0x1 << (AT_DMA_ERR_OFFSET + (x)))
|
||||
|
||||
#define AT_DMA_CHER 0x28 /* Channel Handler Enable Register */
|
||||
#define AT_DMA_ENA(x) (0x1 << (x))
|
||||
#define AT_DMA_SUSP(x) (0x1 << ( 8 + (x)))
|
||||
#define AT_DMA_KEEP(x) (0x1 << (24 + (x)))
|
||||
|
||||
#define AT_DMA_CHDR 0x2C /* Channel Handler Disable Register */
|
||||
#define AT_DMA_DIS(x) (0x1 << (x))
|
||||
#define AT_DMA_RES(x) (0x1 << ( 8 + (x)))
|
||||
|
||||
#define AT_DMA_CHSR 0x30 /* Channel Handler Status Register */
|
||||
#define AT_DMA_EMPT(x) (0x1 << (16 + (x)))
|
||||
#define AT_DMA_STAL(x) (0x1 << (24 + (x)))
|
||||
|
||||
|
||||
#define AT_DMA_CH_REGS_BASE 0x3C /* Channel registers base address */
|
||||
#define ch_regs(x) (AT_DMA_CH_REGS_BASE + (x) * 0x28) /* Channel x base addr */
|
||||
|
||||
/* Hardware register offset for each channel */
|
||||
#define ATC_SADDR_OFFSET 0x00 /* Source Address Register */
|
||||
#define ATC_DADDR_OFFSET 0x04 /* Destination Address Register */
|
||||
#define ATC_DSCR_OFFSET 0x08 /* Descriptor Address Register */
|
||||
#define ATC_CTRLA_OFFSET 0x0C /* Control A Register */
|
||||
#define ATC_CTRLB_OFFSET 0x10 /* Control B Register */
|
||||
#define ATC_CFG_OFFSET 0x14 /* Configuration Register */
|
||||
#define ATC_SPIP_OFFSET 0x18 /* Src PIP Configuration Register */
|
||||
#define ATC_DPIP_OFFSET 0x1C /* Dst PIP Configuration Register */
|
||||
|
||||
|
||||
/* Bitfield definitions */
|
||||
|
||||
/* Bitfields in DSCR */
|
||||
#define ATC_DSCR_IF(i) (0x3 & (i)) /* Dsc feched via AHB-Lite Interface i */
|
||||
|
||||
/* Bitfields in CTRLA */
|
||||
#define ATC_BTSIZE_MAX 0xFFFFUL /* Maximum Buffer Transfer Size */
|
||||
#define ATC_BTSIZE(x) (ATC_BTSIZE_MAX & (x)) /* Buffer Transfer Size */
|
||||
/* Chunck Tranfer size definitions are in at_hdmac.h */
|
||||
#define ATC_SRC_WIDTH_MASK (0x3 << 24) /* Source Single Transfer Size */
|
||||
#define ATC_SRC_WIDTH(x) ((x) << 24)
|
||||
#define ATC_SRC_WIDTH_BYTE (0x0 << 24)
|
||||
#define ATC_SRC_WIDTH_HALFWORD (0x1 << 24)
|
||||
#define ATC_SRC_WIDTH_WORD (0x2 << 24)
|
||||
#define ATC_DST_WIDTH_MASK (0x3 << 28) /* Destination Single Transfer Size */
|
||||
#define ATC_DST_WIDTH(x) ((x) << 28)
|
||||
#define ATC_DST_WIDTH_BYTE (0x0 << 28)
|
||||
#define ATC_DST_WIDTH_HALFWORD (0x1 << 28)
|
||||
#define ATC_DST_WIDTH_WORD (0x2 << 28)
|
||||
#define ATC_DONE (0x1 << 31) /* Tx Done (only written back in descriptor) */
|
||||
|
||||
/* Bitfields in CTRLB */
|
||||
#define ATC_SIF(i) (0x3 & (i)) /* Src tx done via AHB-Lite Interface i */
|
||||
#define ATC_DIF(i) ((0x3 & (i)) << 4) /* Dst tx done via AHB-Lite Interface i */
|
||||
#define ATC_SRC_PIP (0x1 << 8) /* Source Picture-in-Picture enabled */
|
||||
#define ATC_DST_PIP (0x1 << 12) /* Destination Picture-in-Picture enabled */
|
||||
#define ATC_SRC_DSCR_DIS (0x1 << 16) /* Src Descriptor fetch disable */
|
||||
#define ATC_DST_DSCR_DIS (0x1 << 20) /* Dst Descriptor fetch disable */
|
||||
#define ATC_FC_MASK (0x7 << 21) /* Choose Flow Controller */
|
||||
#define ATC_FC_MEM2MEM (0x0 << 21) /* Mem-to-Mem (DMA) */
|
||||
#define ATC_FC_MEM2PER (0x1 << 21) /* Mem-to-Periph (DMA) */
|
||||
#define ATC_FC_PER2MEM (0x2 << 21) /* Periph-to-Mem (DMA) */
|
||||
#define ATC_FC_PER2PER (0x3 << 21) /* Periph-to-Periph (DMA) */
|
||||
#define ATC_FC_PER2MEM_PER (0x4 << 21) /* Periph-to-Mem (Peripheral) */
|
||||
#define ATC_FC_MEM2PER_PER (0x5 << 21) /* Mem-to-Periph (Peripheral) */
|
||||
#define ATC_FC_PER2PER_SRCPER (0x6 << 21) /* Periph-to-Periph (Src Peripheral) */
|
||||
#define ATC_FC_PER2PER_DSTPER (0x7 << 21) /* Periph-to-Periph (Dst Peripheral) */
|
||||
#define ATC_SRC_ADDR_MODE_MASK (0x3 << 24)
|
||||
#define ATC_SRC_ADDR_MODE_INCR (0x0 << 24) /* Incrementing Mode */
|
||||
#define ATC_SRC_ADDR_MODE_DECR (0x1 << 24) /* Decrementing Mode */
|
||||
#define ATC_SRC_ADDR_MODE_FIXED (0x2 << 24) /* Fixed Mode */
|
||||
#define ATC_DST_ADDR_MODE_MASK (0x3 << 28)
|
||||
#define ATC_DST_ADDR_MODE_INCR (0x0 << 28) /* Incrementing Mode */
|
||||
#define ATC_DST_ADDR_MODE_DECR (0x1 << 28) /* Decrementing Mode */
|
||||
#define ATC_DST_ADDR_MODE_FIXED (0x2 << 28) /* Fixed Mode */
|
||||
#define ATC_IEN (0x1 << 30) /* BTC interrupt enable (active low) */
|
||||
#define ATC_AUTO (0x1 << 31) /* Auto multiple buffer tx enable */
|
||||
|
||||
/* Bitfields in CFG */
|
||||
/* are in at_hdmac.h */
|
||||
|
||||
/* Bitfields in SPIP */
|
||||
#define ATC_SPIP_HOLE(x) (0xFFFFU & (x))
|
||||
#define ATC_SPIP_BOUNDARY(x) ((0x3FF & (x)) << 16)
|
||||
|
||||
/* Bitfields in DPIP */
|
||||
#define ATC_DPIP_HOLE(x) (0xFFFFU & (x))
|
||||
#define ATC_DPIP_BOUNDARY(x) ((0x3FF & (x)) << 16)
|
||||
|
||||
|
||||
/*-- descriptors -----------------------------------------------------*/
|
||||
|
||||
/* LLI == Linked List Item; aka DMA buffer descriptor */
|
||||
struct at_lli {
|
||||
/* values that are not changed by hardware */
|
||||
dma_addr_t saddr;
|
||||
dma_addr_t daddr;
|
||||
/* value that may get written back: */
|
||||
u32 ctrla;
|
||||
/* more values that are not changed by hardware */
|
||||
u32 ctrlb;
|
||||
dma_addr_t dscr; /* chain to next lli */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct at_desc - software descriptor
|
||||
* @at_lli: hardware lli structure
|
||||
* @txd: support for the async_tx api
|
||||
* @desc_node: node on the channed descriptors list
|
||||
* @len: total transaction bytecount
|
||||
*/
|
||||
struct at_desc {
|
||||
/* FIRST values the hardware uses */
|
||||
struct at_lli lli;
|
||||
|
||||
/* THEN values for driver housekeeping */
|
||||
struct list_head tx_list;
|
||||
struct dma_async_tx_descriptor txd;
|
||||
struct list_head desc_node;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static inline struct at_desc *
|
||||
txd_to_at_desc(struct dma_async_tx_descriptor *txd)
|
||||
{
|
||||
return container_of(txd, struct at_desc, txd);
|
||||
}
|
||||
|
||||
|
||||
/*-- Channels --------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* struct at_dma_chan - internal representation of an Atmel HDMAC channel
|
||||
* @chan_common: common dmaengine channel object members
|
||||
* @device: parent device
|
||||
* @ch_regs: memory mapped register base
|
||||
* @mask: channel index in a mask
|
||||
* @error_status: transmit error status information from irq handler
|
||||
* to tasklet (use atomic operations)
|
||||
* @tasklet: bottom half to finish transaction work
|
||||
* @lock: serializes enqueue/dequeue operations to descriptors lists
|
||||
* @completed_cookie: identifier for the most recently completed operation
|
||||
* @active_list: list of descriptors dmaengine is being running on
|
||||
* @queue: list of descriptors ready to be submitted to engine
|
||||
* @free_list: list of descriptors usable by the channel
|
||||
* @descs_allocated: records the actual size of the descriptor pool
|
||||
*/
|
||||
struct at_dma_chan {
|
||||
struct dma_chan chan_common;
|
||||
struct at_dma *device;
|
||||
void __iomem *ch_regs;
|
||||
u8 mask;
|
||||
unsigned long error_status;
|
||||
struct tasklet_struct tasklet;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
/* these other elements are all protected by lock */
|
||||
dma_cookie_t completed_cookie;
|
||||
struct list_head active_list;
|
||||
struct list_head queue;
|
||||
struct list_head free_list;
|
||||
unsigned int descs_allocated;
|
||||
};
|
||||
|
||||
#define channel_readl(atchan, name) \
|
||||
__raw_readl((atchan)->ch_regs + ATC_##name##_OFFSET)
|
||||
|
||||
#define channel_writel(atchan, name, val) \
|
||||
__raw_writel((val), (atchan)->ch_regs + ATC_##name##_OFFSET)
|
||||
|
||||
static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan)
|
||||
{
|
||||
return container_of(dchan, struct at_dma_chan, chan_common);
|
||||
}
|
||||
|
||||
|
||||
/*-- Controller ------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* struct at_dma - internal representation of an Atmel HDMA Controller
|
||||
* @chan_common: common dmaengine dma_device object members
|
||||
* @ch_regs: memory mapped register base
|
||||
* @clk: dma controller clock
|
||||
* @all_chan_mask: all channels availlable in a mask
|
||||
* @dma_desc_pool: base of DMA descriptor region (DMA address)
|
||||
* @chan: channels table to store at_dma_chan structures
|
||||
*/
|
||||
struct at_dma {
|
||||
struct dma_device dma_common;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
|
||||
u8 all_chan_mask;
|
||||
|
||||
struct dma_pool *dma_desc_pool;
|
||||
/* AT THE END channels table */
|
||||
struct at_dma_chan chan[0];
|
||||
};
|
||||
|
||||
#define dma_readl(atdma, name) \
|
||||
__raw_readl((atdma)->regs + AT_DMA_##name)
|
||||
#define dma_writel(atdma, name, val) \
|
||||
__raw_writel((val), (atdma)->regs + AT_DMA_##name)
|
||||
|
||||
static inline struct at_dma *to_at_dma(struct dma_device *ddev)
|
||||
{
|
||||
return container_of(ddev, struct at_dma, dma_common);
|
||||
}
|
||||
|
||||
|
||||
/*-- Helper functions ------------------------------------------------*/
|
||||
|
||||
static struct device *chan2dev(struct dma_chan *chan)
|
||||
{
|
||||
return &chan->dev->device;
|
||||
}
|
||||
static struct device *chan2parent(struct dma_chan *chan)
|
||||
{
|
||||
return chan->dev->device.parent;
|
||||
}
|
||||
|
||||
#if defined(VERBOSE_DEBUG)
|
||||
static void vdbg_dump_regs(struct at_dma_chan *atchan)
|
||||
{
|
||||
struct at_dma *atdma = to_at_dma(atchan->chan_common.device);
|
||||
|
||||
dev_err(chan2dev(&atchan->chan_common),
|
||||
" channel %d : imr = 0x%x, chsr = 0x%x\n",
|
||||
atchan->chan_common.chan_id,
|
||||
dma_readl(atdma, EBCIMR),
|
||||
dma_readl(atdma, CHSR));
|
||||
|
||||
dev_err(chan2dev(&atchan->chan_common),
|
||||
" channel: s0x%x d0x%x ctrl0x%x:0x%x cfg0x%x l0x%x\n",
|
||||
channel_readl(atchan, SADDR),
|
||||
channel_readl(atchan, DADDR),
|
||||
channel_readl(atchan, CTRLA),
|
||||
channel_readl(atchan, CTRLB),
|
||||
channel_readl(atchan, CFG),
|
||||
channel_readl(atchan, DSCR));
|
||||
}
|
||||
#else
|
||||
static void vdbg_dump_regs(struct at_dma_chan *atchan) {}
|
||||
#endif
|
||||
|
||||
static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli)
|
||||
{
|
||||
dev_printk(KERN_CRIT, chan2dev(&atchan->chan_common),
|
||||
" desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n",
|
||||
lli->saddr, lli->daddr,
|
||||
lli->ctrla, lli->ctrlb, lli->dscr);
|
||||
}
|
||||
|
||||
|
||||
static void atc_setup_irq(struct at_dma_chan *atchan, int on)
|
||||
{
|
||||
struct at_dma *atdma = to_at_dma(atchan->chan_common.device);
|
||||
u32 ebci;
|
||||
|
||||
/* enable interrupts on buffer chain completion & error */
|
||||
ebci = AT_DMA_CBTC(atchan->chan_common.chan_id)
|
||||
| AT_DMA_ERR(atchan->chan_common.chan_id);
|
||||
if (on)
|
||||
dma_writel(atdma, EBCIER, ebci);
|
||||
else
|
||||
dma_writel(atdma, EBCIDR, ebci);
|
||||
}
|
||||
|
||||
static inline void atc_enable_irq(struct at_dma_chan *atchan)
|
||||
{
|
||||
atc_setup_irq(atchan, 1);
|
||||
}
|
||||
|
||||
static inline void atc_disable_irq(struct at_dma_chan *atchan)
|
||||
{
|
||||
atc_setup_irq(atchan, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* atc_chan_is_enabled - test if given channel is enabled
|
||||
* @atchan: channel we want to test status
|
||||
*/
|
||||
static inline int atc_chan_is_enabled(struct at_dma_chan *atchan)
|
||||
{
|
||||
struct at_dma *atdma = to_at_dma(atchan->chan_common.device);
|
||||
|
||||
return !!(dma_readl(atdma, CHSR) & atchan->mask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set_desc_eol - set end-of-link to descriptor so it will end transfer
|
||||
* @desc: descriptor, signle or at the end of a chain, to end chain on
|
||||
*/
|
||||
static void set_desc_eol(struct at_desc *desc)
|
||||
{
|
||||
desc->lli.ctrlb |= ATC_SRC_DSCR_DIS | ATC_DST_DSCR_DIS;
|
||||
desc->lli.dscr = 0;
|
||||
}
|
||||
|
||||
#endif /* AT_HDMAC_REGS_H */
|
@@ -977,7 +977,6 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
|
||||
{
|
||||
tx->chan = chan;
|
||||
spin_lock_init(&tx->lock);
|
||||
INIT_LIST_HEAD(&tx->tx_list);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_async_tx_descriptor_init);
|
||||
|
||||
|
@@ -38,6 +38,11 @@ module_param(max_channels, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(max_channels,
|
||||
"Maximum number of channels to use (default: all)");
|
||||
|
||||
static unsigned int iterations;
|
||||
module_param(iterations, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(iterations,
|
||||
"Iterations before stopping test (default: infinite)");
|
||||
|
||||
static unsigned int xor_sources = 3;
|
||||
module_param(xor_sources, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(xor_sources,
|
||||
@@ -119,7 +124,7 @@ static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len)
|
||||
buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
|
||||
for ( ; i < start + len; i++)
|
||||
buf[i] = PATTERN_SRC | PATTERN_COPY
|
||||
| (~i & PATTERN_COUNT_MASK);;
|
||||
| (~i & PATTERN_COUNT_MASK);
|
||||
for ( ; i < test_buf_size; i++)
|
||||
buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
|
||||
buf++;
|
||||
@@ -281,7 +286,8 @@ static int dmatest_func(void *data)
|
||||
|
||||
flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
while (!kthread_should_stop()
|
||||
&& !(iterations && total_tests >= iterations)) {
|
||||
struct dma_device *dev = chan->device;
|
||||
struct dma_async_tx_descriptor *tx = NULL;
|
||||
dma_addr_t dma_srcs[src_cnt];
|
||||
@@ -450,6 +456,13 @@ err_srcbuf:
|
||||
err_srcs:
|
||||
pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
|
||||
thread_name, total_tests, failed_tests, ret);
|
||||
|
||||
if (iterations > 0)
|
||||
while (!kthread_should_stop()) {
|
||||
DECLARE_WAIT_QUEUE_HEAD(wait_dmatest_exit);
|
||||
interruptible_sleep_on(&wait_dmatest_exit);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -531,11 +544,11 @@ static int dmatest_add_channel(struct dma_chan *chan)
|
||||
|
||||
if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
|
||||
cnt = dmatest_add_threads(dtc, DMA_MEMCPY);
|
||||
thread_count += cnt > 0 ?: 0;
|
||||
thread_count += cnt > 0 ? cnt : 0;
|
||||
}
|
||||
if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
|
||||
cnt = dmatest_add_threads(dtc, DMA_XOR);
|
||||
thread_count += cnt > 0 ?: 0;
|
||||
thread_count += cnt > 0 ? cnt : 0;
|
||||
}
|
||||
if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) {
|
||||
cnt = dmatest_add_threads(dtc, DMA_PQ);
|
||||
|
@@ -116,7 +116,7 @@ static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc)
|
||||
{
|
||||
struct dw_desc *child;
|
||||
|
||||
list_for_each_entry(child, &desc->txd.tx_list, desc_node)
|
||||
list_for_each_entry(child, &desc->tx_list, desc_node)
|
||||
dma_sync_single_for_cpu(chan2parent(&dwc->chan),
|
||||
child->txd.phys, sizeof(child->lli),
|
||||
DMA_TO_DEVICE);
|
||||
@@ -137,11 +137,11 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
|
||||
dwc_sync_desc_for_cpu(dwc, desc);
|
||||
|
||||
spin_lock_bh(&dwc->lock);
|
||||
list_for_each_entry(child, &desc->txd.tx_list, desc_node)
|
||||
list_for_each_entry(child, &desc->tx_list, desc_node)
|
||||
dev_vdbg(chan2dev(&dwc->chan),
|
||||
"moving child desc %p to freelist\n",
|
||||
child);
|
||||
list_splice_init(&desc->txd.tx_list, &dwc->free_list);
|
||||
list_splice_init(&desc->tx_list, &dwc->free_list);
|
||||
dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc);
|
||||
list_add(&desc->desc_node, &dwc->free_list);
|
||||
spin_unlock_bh(&dwc->lock);
|
||||
@@ -209,19 +209,28 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc)
|
||||
param = txd->callback_param;
|
||||
|
||||
dwc_sync_desc_for_cpu(dwc, desc);
|
||||
list_splice_init(&txd->tx_list, &dwc->free_list);
|
||||
list_splice_init(&desc->tx_list, &dwc->free_list);
|
||||
list_move(&desc->desc_node, &dwc->free_list);
|
||||
|
||||
/*
|
||||
* We use dma_unmap_page() regardless of how the buffers were
|
||||
* mapped before they were submitted...
|
||||
*/
|
||||
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP))
|
||||
dma_unmap_page(chan2parent(&dwc->chan), desc->lli.dar,
|
||||
desc->len, DMA_FROM_DEVICE);
|
||||
if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP))
|
||||
dma_unmap_page(chan2parent(&dwc->chan), desc->lli.sar,
|
||||
desc->len, DMA_TO_DEVICE);
|
||||
if (!dwc->chan.private) {
|
||||
struct device *parent = chan2parent(&dwc->chan);
|
||||
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
|
||||
if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
|
||||
dma_unmap_single(parent, desc->lli.dar,
|
||||
desc->len, DMA_FROM_DEVICE);
|
||||
else
|
||||
dma_unmap_page(parent, desc->lli.dar,
|
||||
desc->len, DMA_FROM_DEVICE);
|
||||
}
|
||||
if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
|
||||
if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
|
||||
dma_unmap_single(parent, desc->lli.sar,
|
||||
desc->len, DMA_TO_DEVICE);
|
||||
else
|
||||
dma_unmap_page(parent, desc->lli.sar,
|
||||
desc->len, DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The API requires that no submissions are done from a
|
||||
@@ -289,7 +298,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
|
||||
/* This one is currently in progress */
|
||||
return;
|
||||
|
||||
list_for_each_entry(child, &desc->txd.tx_list, desc_node)
|
||||
list_for_each_entry(child, &desc->tx_list, desc_node)
|
||||
if (child->lli.llp == llp)
|
||||
/* Currently in progress */
|
||||
return;
|
||||
@@ -356,7 +365,7 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
|
||||
dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
|
||||
" cookie: %d\n", bad_desc->txd.cookie);
|
||||
dwc_dump_lli(dwc, &bad_desc->lli);
|
||||
list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node)
|
||||
list_for_each_entry(child, &bad_desc->tx_list, desc_node)
|
||||
dwc_dump_lli(dwc, &child->lli);
|
||||
|
||||
/* Pretend the descriptor completed successfully */
|
||||
@@ -608,7 +617,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
||||
prev->txd.phys, sizeof(prev->lli),
|
||||
DMA_TO_DEVICE);
|
||||
list_add_tail(&desc->desc_node,
|
||||
&first->txd.tx_list);
|
||||
&first->tx_list);
|
||||
}
|
||||
prev = desc;
|
||||
}
|
||||
@@ -658,8 +667,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
reg_width = dws->reg_width;
|
||||
prev = first = NULL;
|
||||
|
||||
sg_len = dma_map_sg(chan2parent(chan), sgl, sg_len, direction);
|
||||
|
||||
switch (direction) {
|
||||
case DMA_TO_DEVICE:
|
||||
ctllo = (DWC_DEFAULT_CTLLO
|
||||
@@ -700,7 +707,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
sizeof(prev->lli),
|
||||
DMA_TO_DEVICE);
|
||||
list_add_tail(&desc->desc_node,
|
||||
&first->txd.tx_list);
|
||||
&first->tx_list);
|
||||
}
|
||||
prev = desc;
|
||||
total_len += len;
|
||||
@@ -746,7 +753,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
sizeof(prev->lli),
|
||||
DMA_TO_DEVICE);
|
||||
list_add_tail(&desc->desc_node,
|
||||
&first->txd.tx_list);
|
||||
&first->tx_list);
|
||||
}
|
||||
prev = desc;
|
||||
total_len += len;
|
||||
@@ -902,6 +909,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
|
||||
break;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&desc->tx_list);
|
||||
dma_async_tx_descriptor_init(&desc->txd, chan);
|
||||
desc->txd.tx_submit = dwc_tx_submit;
|
||||
desc->txd.flags = DMA_CTRL_ACK;
|
||||
|
@@ -217,6 +217,7 @@ struct dw_desc {
|
||||
|
||||
/* THEN values for driver housekeeping */
|
||||
struct list_head desc_node;
|
||||
struct list_head tx_list;
|
||||
struct dma_async_tx_descriptor txd;
|
||||
size_t len;
|
||||
};
|
||||
|
@@ -12,6 +12,11 @@
|
||||
* also fit for MPC8560, MPC8555, MPC8548, MPC8641, and etc.
|
||||
* The support for MPC8349 DMA contorller is also added.
|
||||
*
|
||||
* This driver instructs the DMA controller to issue the PCI Read Multiple
|
||||
* command for PCI read operations, instead of using the default PCI Read Line
|
||||
* command. Please be aware that this setting may result in read pre-fetching
|
||||
* on some platforms.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
@@ -29,6 +34,7 @@
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/fsldma.h>
|
||||
#include "fsldma.h"
|
||||
|
||||
static void dma_init(struct fsl_dma_chan *fsl_chan)
|
||||
@@ -49,9 +55,10 @@ static void dma_init(struct fsl_dma_chan *fsl_chan)
|
||||
case FSL_DMA_IP_83XX:
|
||||
/* Set the channel to below modes:
|
||||
* EOTIE - End-of-transfer interrupt enable
|
||||
* PRC_RM - PCI read multiple
|
||||
*/
|
||||
DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE,
|
||||
32);
|
||||
DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE
|
||||
| FSL_DMA_MR_PRC_RM, 32);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -136,15 +143,16 @@ static int dma_is_idle(struct fsl_dma_chan *fsl_chan)
|
||||
|
||||
static void dma_start(struct fsl_dma_chan *fsl_chan)
|
||||
{
|
||||
u32 mr_set = 0;;
|
||||
u32 mr_set = 0;
|
||||
|
||||
if (fsl_chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
|
||||
DMA_OUT(fsl_chan, &fsl_chan->reg_base->bcr, 0, 32);
|
||||
mr_set |= FSL_DMA_MR_EMP_EN;
|
||||
} else
|
||||
} else if ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
|
||||
DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
|
||||
DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
|
||||
& ~FSL_DMA_MR_EMP_EN, 32);
|
||||
}
|
||||
|
||||
if (fsl_chan->feature & FSL_DMA_CHAN_START_EXT)
|
||||
mr_set |= FSL_DMA_MR_EMS_EN;
|
||||
@@ -272,29 +280,41 @@ static void fsl_chan_set_dest_loop_size(struct fsl_dma_chan *fsl_chan, int size)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_chan_set_request_count - Set DMA Request Count for external control
|
||||
* @fsl_chan : Freescale DMA channel
|
||||
* @size : Number of bytes to transfer in a single request
|
||||
*
|
||||
* The Freescale DMA channel can be controlled by the external signal DREQ#.
|
||||
* The DMA request count is how many bytes are allowed to transfer before
|
||||
* pausing the channel, after which a new assertion of DREQ# resumes channel
|
||||
* operation.
|
||||
*
|
||||
* A size of 0 disables external pause control. The maximum size is 1024.
|
||||
*/
|
||||
static void fsl_chan_set_request_count(struct fsl_dma_chan *fsl_chan, int size)
|
||||
{
|
||||
BUG_ON(size > 1024);
|
||||
DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
|
||||
DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
|
||||
| ((__ilog2(size) << 24) & 0x0f000000),
|
||||
32);
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_chan_toggle_ext_pause - Toggle channel external pause status
|
||||
* @fsl_chan : Freescale DMA channel
|
||||
* @size : Pause control size, 0 for disable external pause control.
|
||||
* The maximum is 1024.
|
||||
* @enable : 0 is disabled, 1 is enabled.
|
||||
*
|
||||
* The Freescale DMA channel can be controlled by the external
|
||||
* signal DREQ#. The pause control size is how many bytes are allowed
|
||||
* to transfer before pausing the channel, after which a new assertion
|
||||
* of DREQ# resumes channel operation.
|
||||
* The Freescale DMA channel can be controlled by the external signal DREQ#.
|
||||
* The DMA Request Count feature should be used in addition to this feature
|
||||
* to set the number of bytes to transfer before pausing the channel.
|
||||
*/
|
||||
static void fsl_chan_toggle_ext_pause(struct fsl_dma_chan *fsl_chan, int size)
|
||||
static void fsl_chan_toggle_ext_pause(struct fsl_dma_chan *fsl_chan, int enable)
|
||||
{
|
||||
if (size > 1024)
|
||||
return;
|
||||
|
||||
if (size) {
|
||||
DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
|
||||
DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
|
||||
| ((__ilog2(size) << 24) & 0x0f000000),
|
||||
32);
|
||||
if (enable)
|
||||
fsl_chan->feature |= FSL_DMA_CHAN_PAUSE_EXT;
|
||||
} else
|
||||
else
|
||||
fsl_chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT;
|
||||
}
|
||||
|
||||
@@ -319,7 +339,8 @@ static void fsl_chan_toggle_ext_start(struct fsl_dma_chan *fsl_chan, int enable)
|
||||
static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
struct fsl_dma_chan *fsl_chan = to_fsl_chan(tx->chan);
|
||||
struct fsl_desc_sw *desc;
|
||||
struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
|
||||
struct fsl_desc_sw *child;
|
||||
unsigned long flags;
|
||||
dma_cookie_t cookie;
|
||||
|
||||
@@ -327,7 +348,7 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
spin_lock_irqsave(&fsl_chan->desc_lock, flags);
|
||||
|
||||
cookie = fsl_chan->common.cookie;
|
||||
list_for_each_entry(desc, &tx->tx_list, node) {
|
||||
list_for_each_entry(child, &desc->tx_list, node) {
|
||||
cookie++;
|
||||
if (cookie < 0)
|
||||
cookie = 1;
|
||||
@@ -336,8 +357,8 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
}
|
||||
|
||||
fsl_chan->common.cookie = cookie;
|
||||
append_ld_queue(fsl_chan, tx_to_fsl_desc(tx));
|
||||
list_splice_init(&tx->tx_list, fsl_chan->ld_queue.prev);
|
||||
append_ld_queue(fsl_chan, desc);
|
||||
list_splice_init(&desc->tx_list, fsl_chan->ld_queue.prev);
|
||||
|
||||
spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
|
||||
|
||||
@@ -359,6 +380,7 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor(
|
||||
desc_sw = dma_pool_alloc(fsl_chan->desc_pool, GFP_ATOMIC, &pdesc);
|
||||
if (desc_sw) {
|
||||
memset(desc_sw, 0, sizeof(struct fsl_desc_sw));
|
||||
INIT_LIST_HEAD(&desc_sw->tx_list);
|
||||
dma_async_tx_descriptor_init(&desc_sw->async_tx,
|
||||
&fsl_chan->common);
|
||||
desc_sw->async_tx.tx_submit = fsl_dma_tx_submit;
|
||||
@@ -448,7 +470,7 @@ fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags)
|
||||
new->async_tx.flags = flags;
|
||||
|
||||
/* Insert the link descriptor to the LD ring */
|
||||
list_add_tail(&new->node, &new->async_tx.tx_list);
|
||||
list_add_tail(&new->node, &new->tx_list);
|
||||
|
||||
/* Set End-of-link to the last link descriptor of new list*/
|
||||
set_ld_eol(fsl_chan, new);
|
||||
@@ -506,7 +528,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
|
||||
dma_dest += copy;
|
||||
|
||||
/* Insert the link descriptor to the LD ring */
|
||||
list_add_tail(&new->node, &first->async_tx.tx_list);
|
||||
list_add_tail(&new->node, &first->tx_list);
|
||||
} while (len);
|
||||
|
||||
new->async_tx.flags = flags; /* client is in control of this ack */
|
||||
@@ -521,7 +543,7 @@ fail:
|
||||
if (!first)
|
||||
return NULL;
|
||||
|
||||
list = &first->async_tx.tx_list;
|
||||
list = &first->tx_list;
|
||||
list_for_each_entry_safe_reverse(new, prev, list, node) {
|
||||
list_del(&new->node);
|
||||
dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys);
|
||||
@@ -530,6 +552,229 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
|
||||
* @chan: DMA channel
|
||||
* @sgl: scatterlist to transfer to/from
|
||||
* @sg_len: number of entries in @scatterlist
|
||||
* @direction: DMA direction
|
||||
* @flags: DMAEngine flags
|
||||
*
|
||||
* Prepare a set of descriptors for a DMA_SLAVE transaction. Following the
|
||||
* DMA_SLAVE API, this gets the device-specific information from the
|
||||
* chan->private variable.
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
|
||||
struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
|
||||
enum dma_data_direction direction, unsigned long flags)
|
||||
{
|
||||
struct fsl_dma_chan *fsl_chan;
|
||||
struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
|
||||
struct fsl_dma_slave *slave;
|
||||
struct list_head *tx_list;
|
||||
size_t copy;
|
||||
|
||||
int i;
|
||||
struct scatterlist *sg;
|
||||
size_t sg_used;
|
||||
size_t hw_used;
|
||||
struct fsl_dma_hw_addr *hw;
|
||||
dma_addr_t dma_dst, dma_src;
|
||||
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
if (!chan->private)
|
||||
return NULL;
|
||||
|
||||
fsl_chan = to_fsl_chan(chan);
|
||||
slave = chan->private;
|
||||
|
||||
if (list_empty(&slave->addresses))
|
||||
return NULL;
|
||||
|
||||
hw = list_first_entry(&slave->addresses, struct fsl_dma_hw_addr, entry);
|
||||
hw_used = 0;
|
||||
|
||||
/*
|
||||
* Build the hardware transaction to copy from the scatterlist to
|
||||
* the hardware, or from the hardware to the scatterlist
|
||||
*
|
||||
* If you are copying from the hardware to the scatterlist and it
|
||||
* takes two hardware entries to fill an entire page, then both
|
||||
* hardware entries will be coalesced into the same page
|
||||
*
|
||||
* If you are copying from the scatterlist to the hardware and a
|
||||
* single page can fill two hardware entries, then the data will
|
||||
* be read out of the page into the first hardware entry, and so on
|
||||
*/
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
sg_used = 0;
|
||||
|
||||
/* Loop until the entire scatterlist entry is used */
|
||||
while (sg_used < sg_dma_len(sg)) {
|
||||
|
||||
/*
|
||||
* If we've used up the current hardware address/length
|
||||
* pair, we need to load a new one
|
||||
*
|
||||
* This is done in a while loop so that descriptors with
|
||||
* length == 0 will be skipped
|
||||
*/
|
||||
while (hw_used >= hw->length) {
|
||||
|
||||
/*
|
||||
* If the current hardware entry is the last
|
||||
* entry in the list, we're finished
|
||||
*/
|
||||
if (list_is_last(&hw->entry, &slave->addresses))
|
||||
goto finished;
|
||||
|
||||
/* Get the next hardware address/length pair */
|
||||
hw = list_entry(hw->entry.next,
|
||||
struct fsl_dma_hw_addr, entry);
|
||||
hw_used = 0;
|
||||
}
|
||||
|
||||
/* Allocate the link descriptor from DMA pool */
|
||||
new = fsl_dma_alloc_descriptor(fsl_chan);
|
||||
if (!new) {
|
||||
dev_err(fsl_chan->dev, "No free memory for "
|
||||
"link descriptor\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef FSL_DMA_LD_DEBUG
|
||||
dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Calculate the maximum number of bytes to transfer,
|
||||
* making sure it is less than the DMA controller limit
|
||||
*/
|
||||
copy = min_t(size_t, sg_dma_len(sg) - sg_used,
|
||||
hw->length - hw_used);
|
||||
copy = min_t(size_t, copy, FSL_DMA_BCR_MAX_CNT);
|
||||
|
||||
/*
|
||||
* DMA_FROM_DEVICE
|
||||
* from the hardware to the scatterlist
|
||||
*
|
||||
* DMA_TO_DEVICE
|
||||
* from the scatterlist to the hardware
|
||||
*/
|
||||
if (direction == DMA_FROM_DEVICE) {
|
||||
dma_src = hw->address + hw_used;
|
||||
dma_dst = sg_dma_address(sg) + sg_used;
|
||||
} else {
|
||||
dma_src = sg_dma_address(sg) + sg_used;
|
||||
dma_dst = hw->address + hw_used;
|
||||
}
|
||||
|
||||
/* Fill in the descriptor */
|
||||
set_desc_cnt(fsl_chan, &new->hw, copy);
|
||||
set_desc_src(fsl_chan, &new->hw, dma_src);
|
||||
set_desc_dest(fsl_chan, &new->hw, dma_dst);
|
||||
|
||||
/*
|
||||
* If this is not the first descriptor, chain the
|
||||
* current descriptor after the previous descriptor
|
||||
*/
|
||||
if (!first) {
|
||||
first = new;
|
||||
} else {
|
||||
set_desc_next(fsl_chan, &prev->hw,
|
||||
new->async_tx.phys);
|
||||
}
|
||||
|
||||
new->async_tx.cookie = 0;
|
||||
async_tx_ack(&new->async_tx);
|
||||
|
||||
prev = new;
|
||||
sg_used += copy;
|
||||
hw_used += copy;
|
||||
|
||||
/* Insert the link descriptor into the LD ring */
|
||||
list_add_tail(&new->node, &first->tx_list);
|
||||
}
|
||||
}
|
||||
|
||||
finished:
|
||||
|
||||
/* All of the hardware address/length pairs had length == 0 */
|
||||
if (!first || !new)
|
||||
return NULL;
|
||||
|
||||
new->async_tx.flags = flags;
|
||||
new->async_tx.cookie = -EBUSY;
|
||||
|
||||
/* Set End-of-link to the last link descriptor of new list */
|
||||
set_ld_eol(fsl_chan, new);
|
||||
|
||||
/* Enable extra controller features */
|
||||
if (fsl_chan->set_src_loop_size)
|
||||
fsl_chan->set_src_loop_size(fsl_chan, slave->src_loop_size);
|
||||
|
||||
if (fsl_chan->set_dest_loop_size)
|
||||
fsl_chan->set_dest_loop_size(fsl_chan, slave->dst_loop_size);
|
||||
|
||||
if (fsl_chan->toggle_ext_start)
|
||||
fsl_chan->toggle_ext_start(fsl_chan, slave->external_start);
|
||||
|
||||
if (fsl_chan->toggle_ext_pause)
|
||||
fsl_chan->toggle_ext_pause(fsl_chan, slave->external_pause);
|
||||
|
||||
if (fsl_chan->set_request_count)
|
||||
fsl_chan->set_request_count(fsl_chan, slave->request_count);
|
||||
|
||||
return &first->async_tx;
|
||||
|
||||
fail:
|
||||
/* If first was not set, then we failed to allocate the very first
|
||||
* descriptor, and we're done */
|
||||
if (!first)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* First is set, so all of the descriptors we allocated have been added
|
||||
* to first->tx_list, INCLUDING "first" itself. Therefore we
|
||||
* must traverse the list backwards freeing each descriptor in turn
|
||||
*
|
||||
* We're re-using variables for the loop, oh well
|
||||
*/
|
||||
tx_list = &first->tx_list;
|
||||
list_for_each_entry_safe_reverse(new, prev, tx_list, node) {
|
||||
list_del_init(&new->node);
|
||||
dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fsl_dma_device_terminate_all(struct dma_chan *chan)
|
||||
{
|
||||
struct fsl_dma_chan *fsl_chan;
|
||||
struct fsl_desc_sw *desc, *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
if (!chan)
|
||||
return;
|
||||
|
||||
fsl_chan = to_fsl_chan(chan);
|
||||
|
||||
/* Halt the DMA engine */
|
||||
dma_halt(fsl_chan);
|
||||
|
||||
spin_lock_irqsave(&fsl_chan->desc_lock, flags);
|
||||
|
||||
/* Remove and free all of the descriptors in the LD queue */
|
||||
list_for_each_entry_safe(desc, tmp, &fsl_chan->ld_queue, node) {
|
||||
list_del(&desc->node);
|
||||
dma_pool_free(fsl_chan->desc_pool, desc, desc->async_tx.phys);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_dma_update_completed_cookie - Update the completed cookie.
|
||||
* @fsl_chan : Freescale DMA channel
|
||||
@@ -871,11 +1116,12 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
|
||||
|
||||
switch (new_fsl_chan->feature & FSL_DMA_IP_MASK) {
|
||||
case FSL_DMA_IP_85XX:
|
||||
new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start;
|
||||
new_fsl_chan->toggle_ext_pause = fsl_chan_toggle_ext_pause;
|
||||
case FSL_DMA_IP_83XX:
|
||||
new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start;
|
||||
new_fsl_chan->set_src_loop_size = fsl_chan_set_src_loop_size;
|
||||
new_fsl_chan->set_dest_loop_size = fsl_chan_set_dest_loop_size;
|
||||
new_fsl_chan->set_request_count = fsl_chan_set_request_count;
|
||||
}
|
||||
|
||||
spin_lock_init(&new_fsl_chan->desc_lock);
|
||||
@@ -955,12 +1201,15 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
|
||||
|
||||
dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask);
|
||||
dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask);
|
||||
dma_cap_set(DMA_SLAVE, fdev->common.cap_mask);
|
||||
fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources;
|
||||
fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources;
|
||||
fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt;
|
||||
fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy;
|
||||
fdev->common.device_is_tx_complete = fsl_dma_is_complete;
|
||||
fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
|
||||
fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg;
|
||||
fdev->common.device_terminate_all = fsl_dma_device_terminate_all;
|
||||
fdev->common.dev = &dev->dev;
|
||||
|
||||
fdev->irq = irq_of_parse_and_map(dev->node, 0);
|
||||
|
@@ -38,6 +38,7 @@
|
||||
|
||||
/* Special MR definition for MPC8349 */
|
||||
#define FSL_DMA_MR_EOTIE 0x00000080
|
||||
#define FSL_DMA_MR_PRC_RM 0x00000800
|
||||
|
||||
#define FSL_DMA_SR_CH 0x00000020
|
||||
#define FSL_DMA_SR_PE 0x00000010
|
||||
@@ -89,6 +90,7 @@ struct fsl_dma_ld_hw {
|
||||
struct fsl_desc_sw {
|
||||
struct fsl_dma_ld_hw hw;
|
||||
struct list_head node;
|
||||
struct list_head tx_list;
|
||||
struct dma_async_tx_descriptor async_tx;
|
||||
struct list_head *ld;
|
||||
void *priv;
|
||||
@@ -142,10 +144,11 @@ struct fsl_dma_chan {
|
||||
struct tasklet_struct tasklet;
|
||||
u32 feature;
|
||||
|
||||
void (*toggle_ext_pause)(struct fsl_dma_chan *fsl_chan, int size);
|
||||
void (*toggle_ext_pause)(struct fsl_dma_chan *fsl_chan, int enable);
|
||||
void (*toggle_ext_start)(struct fsl_dma_chan *fsl_chan, int enable);
|
||||
void (*set_src_loop_size)(struct fsl_dma_chan *fsl_chan, int size);
|
||||
void (*set_dest_loop_size)(struct fsl_dma_chan *fsl_chan, int size);
|
||||
void (*set_request_count)(struct fsl_dma_chan *fsl_chan, int size);
|
||||
};
|
||||
|
||||
#define to_fsl_chan(chan) container_of(chan, struct fsl_dma_chan, common)
|
||||
|
@@ -251,12 +251,12 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie);
|
||||
|
||||
/* write address into NextDescriptor field of last desc in chain */
|
||||
first = to_ioat_desc(tx->tx_list.next);
|
||||
first = to_ioat_desc(desc->tx_list.next);
|
||||
chain_tail = to_ioat_desc(ioat->used_desc.prev);
|
||||
/* make descriptor updates globally visible before chaining */
|
||||
wmb();
|
||||
chain_tail->hw->next = first->txd.phys;
|
||||
list_splice_tail_init(&tx->tx_list, &ioat->used_desc);
|
||||
list_splice_tail_init(&desc->tx_list, &ioat->used_desc);
|
||||
dump_desc_dbg(ioat, chain_tail);
|
||||
dump_desc_dbg(ioat, first);
|
||||
|
||||
@@ -298,6 +298,7 @@ ioat_dma_alloc_descriptor(struct ioat_dma_chan *ioat, gfp_t flags)
|
||||
|
||||
memset(desc, 0, sizeof(*desc));
|
||||
|
||||
INIT_LIST_HEAD(&desc_sw->tx_list);
|
||||
dma_async_tx_descriptor_init(&desc_sw->txd, &ioat->base.common);
|
||||
desc_sw->txd.tx_submit = ioat1_tx_submit;
|
||||
desc_sw->hw = desc;
|
||||
@@ -522,7 +523,7 @@ ioat1_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dest,
|
||||
|
||||
desc->txd.flags = flags;
|
||||
desc->len = total_len;
|
||||
list_splice(&chain, &desc->txd.tx_list);
|
||||
list_splice(&chain, &desc->tx_list);
|
||||
hw->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT);
|
||||
hw->ctl_f.compl_write = 1;
|
||||
hw->tx_cnt = tx_cnt;
|
||||
|
@@ -171,7 +171,7 @@ ioat_is_complete(struct dma_chan *c, dma_cookie_t cookie,
|
||||
* struct ioat_desc_sw - wrapper around hardware descriptor
|
||||
* @hw: hardware DMA descriptor (for memcpy)
|
||||
* @node: this descriptor will either be on the free list,
|
||||
* or attached to a transaction list (async_tx.tx_list)
|
||||
* or attached to a transaction list (tx_list)
|
||||
* @txd: the generic software descriptor for all engines
|
||||
* @id: identifier for debug
|
||||
*/
|
||||
@@ -179,6 +179,7 @@ struct ioat_desc_sw {
|
||||
struct ioat_dma_descriptor *hw;
|
||||
struct list_head node;
|
||||
size_t len;
|
||||
struct list_head tx_list;
|
||||
struct dma_async_tx_descriptor txd;
|
||||
#ifdef DEBUG
|
||||
int id;
|
||||
|
@@ -397,11 +397,12 @@ static struct ioat_ring_ent *ioat2_alloc_ring_ent(struct dma_chan *chan, gfp_t f
|
||||
return NULL;
|
||||
memset(hw, 0, sizeof(*hw));
|
||||
|
||||
desc = kzalloc(sizeof(*desc), flags);
|
||||
desc = kmem_cache_alloc(ioat2_cache, flags);
|
||||
if (!desc) {
|
||||
pci_pool_free(dma->dma_pool, hw, phys);
|
||||
return NULL;
|
||||
}
|
||||
memset(desc, 0, sizeof(*desc));
|
||||
|
||||
dma_async_tx_descriptor_init(&desc->txd, chan);
|
||||
desc->txd.tx_submit = ioat2_tx_submit_unlock;
|
||||
@@ -416,7 +417,7 @@ static void ioat2_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *cha
|
||||
|
||||
dma = to_ioatdma_device(chan->device);
|
||||
pci_pool_free(dma->dma_pool, desc->hw, desc->txd.phys);
|
||||
kfree(desc);
|
||||
kmem_cache_free(ioat2_cache, desc);
|
||||
}
|
||||
|
||||
static struct ioat_ring_ent **ioat2_alloc_ring(struct dma_chan *c, int order, gfp_t flags)
|
||||
|
@@ -142,8 +142,8 @@ struct ioat_ring_ent {
|
||||
struct ioat_pq_update_descriptor *pqu;
|
||||
struct ioat_raw_descriptor *raw;
|
||||
};
|
||||
struct dma_async_tx_descriptor txd;
|
||||
size_t len;
|
||||
struct dma_async_tx_descriptor txd;
|
||||
enum sum_check_flags *result;
|
||||
#ifdef DEBUG
|
||||
int id;
|
||||
@@ -186,4 +186,5 @@ void __ioat2_issue_pending(struct ioat2_dma_chan *ioat);
|
||||
void ioat2_cleanup_tasklet(unsigned long data);
|
||||
void ioat2_timer_event(unsigned long data);
|
||||
extern struct kobj_type ioat2_ktype;
|
||||
extern struct kmem_cache *ioat2_cache;
|
||||
#endif /* IOATDMA_V2_H */
|
||||
|
@@ -83,6 +83,8 @@ static int ioat_dca_enabled = 1;
|
||||
module_param(ioat_dca_enabled, int, 0644);
|
||||
MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)");
|
||||
|
||||
struct kmem_cache *ioat2_cache;
|
||||
|
||||
#define DRV_NAME "ioatdma"
|
||||
|
||||
static struct pci_driver ioat_pci_driver = {
|
||||
@@ -182,15 +184,27 @@ static void __devexit ioat_remove(struct pci_dev *pdev)
|
||||
|
||||
static int __init ioat_init_module(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_info("%s: Intel(R) QuickData Technology Driver %s\n",
|
||||
DRV_NAME, IOAT_DMA_VERSION);
|
||||
|
||||
return pci_register_driver(&ioat_pci_driver);
|
||||
ioat2_cache = kmem_cache_create("ioat2", sizeof(struct ioat_ring_ent),
|
||||
0, SLAB_HWCACHE_ALIGN, NULL);
|
||||
if (!ioat2_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pci_register_driver(&ioat_pci_driver);
|
||||
if (err)
|
||||
kmem_cache_destroy(ioat2_cache);
|
||||
|
||||
return err;
|
||||
}
|
||||
module_init(ioat_init_module);
|
||||
|
||||
static void __exit ioat_exit_module(void)
|
||||
{
|
||||
pci_unregister_driver(&ioat_pci_driver);
|
||||
kmem_cache_destroy(ioat2_cache);
|
||||
}
|
||||
module_exit(ioat_exit_module);
|
||||
|
@@ -421,7 +421,7 @@ retry:
|
||||
}
|
||||
alloc_tail->group_head = alloc_start;
|
||||
alloc_tail->async_tx.cookie = -EBUSY;
|
||||
list_splice(&chain, &alloc_tail->async_tx.tx_list);
|
||||
list_splice(&chain, &alloc_tail->tx_list);
|
||||
iop_chan->last_used = last_used;
|
||||
iop_desc_clear_next_desc(alloc_start);
|
||||
iop_desc_clear_next_desc(alloc_tail);
|
||||
@@ -480,7 +480,7 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
|
||||
old_chain_tail = list_entry(iop_chan->chain.prev,
|
||||
struct iop_adma_desc_slot, chain_node);
|
||||
list_splice_init(&sw_desc->async_tx.tx_list,
|
||||
list_splice_init(&sw_desc->tx_list,
|
||||
&old_chain_tail->chain_node);
|
||||
|
||||
/* fix up the hardware chain */
|
||||
@@ -547,6 +547,7 @@ static int iop_adma_alloc_chan_resources(struct dma_chan *chan)
|
||||
|
||||
dma_async_tx_descriptor_init(&slot->async_tx, chan);
|
||||
slot->async_tx.tx_submit = iop_adma_tx_submit;
|
||||
INIT_LIST_HEAD(&slot->tx_list);
|
||||
INIT_LIST_HEAD(&slot->chain_node);
|
||||
INIT_LIST_HEAD(&slot->slot_node);
|
||||
hw_desc = (char *) iop_chan->device->dma_desc_pool;
|
||||
@@ -1642,7 +1643,7 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan)
|
||||
if (sw_desc) {
|
||||
grp_start = sw_desc->group_head;
|
||||
|
||||
list_splice_init(&sw_desc->async_tx.tx_list, &iop_chan->chain);
|
||||
list_splice_init(&sw_desc->tx_list, &iop_chan->chain);
|
||||
async_tx_ack(&sw_desc->async_tx);
|
||||
iop_desc_init_memcpy(grp_start, 0);
|
||||
iop_desc_set_byte_count(grp_start, iop_chan, 0);
|
||||
@@ -1698,7 +1699,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan)
|
||||
sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op);
|
||||
if (sw_desc) {
|
||||
grp_start = sw_desc->group_head;
|
||||
list_splice_init(&sw_desc->async_tx.tx_list, &iop_chan->chain);
|
||||
list_splice_init(&sw_desc->tx_list, &iop_chan->chain);
|
||||
async_tx_ack(&sw_desc->async_tx);
|
||||
iop_desc_init_null_xor(grp_start, 2, 0);
|
||||
iop_desc_set_byte_count(grp_start, iop_chan, 0);
|
||||
|
@@ -517,7 +517,7 @@ retry:
|
||||
}
|
||||
alloc_tail->group_head = alloc_start;
|
||||
alloc_tail->async_tx.cookie = -EBUSY;
|
||||
list_splice(&chain, &alloc_tail->async_tx.tx_list);
|
||||
list_splice(&chain, &alloc_tail->tx_list);
|
||||
mv_chan->last_used = last_used;
|
||||
mv_desc_clear_next_desc(alloc_start);
|
||||
mv_desc_clear_next_desc(alloc_tail);
|
||||
@@ -565,14 +565,14 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
cookie = mv_desc_assign_cookie(mv_chan, sw_desc);
|
||||
|
||||
if (list_empty(&mv_chan->chain))
|
||||
list_splice_init(&sw_desc->async_tx.tx_list, &mv_chan->chain);
|
||||
list_splice_init(&sw_desc->tx_list, &mv_chan->chain);
|
||||
else {
|
||||
new_hw_chain = 0;
|
||||
|
||||
old_chain_tail = list_entry(mv_chan->chain.prev,
|
||||
struct mv_xor_desc_slot,
|
||||
chain_node);
|
||||
list_splice_init(&grp_start->async_tx.tx_list,
|
||||
list_splice_init(&grp_start->tx_list,
|
||||
&old_chain_tail->chain_node);
|
||||
|
||||
if (!mv_can_chain(grp_start))
|
||||
@@ -632,6 +632,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
|
||||
slot->async_tx.tx_submit = mv_xor_tx_submit;
|
||||
INIT_LIST_HEAD(&slot->chain_node);
|
||||
INIT_LIST_HEAD(&slot->slot_node);
|
||||
INIT_LIST_HEAD(&slot->tx_list);
|
||||
hw_desc = (char *) mv_chan->device->dma_desc_pool;
|
||||
slot->async_tx.phys =
|
||||
(dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE];
|
||||
@@ -1176,7 +1177,7 @@ static int __devinit mv_xor_probe(struct platform_device *pdev)
|
||||
if (dma_has_cap(DMA_MEMSET, dma_dev->cap_mask))
|
||||
dma_dev->device_prep_dma_memset = mv_xor_prep_dma_memset;
|
||||
if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
|
||||
dma_dev->max_xor = 8; ;
|
||||
dma_dev->max_xor = 8;
|
||||
dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor;
|
||||
}
|
||||
|
||||
|
@@ -126,9 +126,8 @@ struct mv_xor_chan {
|
||||
* @idx: pool index
|
||||
* @unmap_src_cnt: number of xor sources
|
||||
* @unmap_len: transaction bytecount
|
||||
* @tx_list: list of slots that make up a multi-descriptor transaction
|
||||
* @async_tx: support for the async_tx api
|
||||
* @group_list: list of slots that make up a multi-descriptor transaction
|
||||
* for example transfer lengths larger than the supported hw max
|
||||
* @xor_check_result: result of zero sum
|
||||
* @crc32_result: result crc calculation
|
||||
*/
|
||||
@@ -145,6 +144,7 @@ struct mv_xor_desc_slot {
|
||||
u16 unmap_src_cnt;
|
||||
u32 value;
|
||||
size_t unmap_len;
|
||||
struct list_head tx_list;
|
||||
struct dma_async_tx_descriptor async_tx;
|
||||
union {
|
||||
u32 *xor_check_result;
|
||||
|
1356
drivers/dma/txx9dmac.c
Normal file
1356
drivers/dma/txx9dmac.c
Normal file
File diff suppressed because it is too large
Load Diff
308
drivers/dma/txx9dmac.h
Normal file
308
drivers/dma/txx9dmac.h
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Driver for the TXx9 SoC DMA Controller
|
||||
*
|
||||
* Copyright (C) 2009 Atsushi Nemoto
|
||||
*
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef TXX9DMAC_H
|
||||
#define TXX9DMAC_H
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <asm/txx9/dmac.h>
|
||||
|
||||
/*
|
||||
* Design Notes:
|
||||
*
|
||||
* This DMAC have four channels and one FIFO buffer. Each channel can
|
||||
* be configured for memory-memory or device-memory transfer, but only
|
||||
* one channel can do alignment-free memory-memory transfer at a time
|
||||
* while the channel should occupy the FIFO buffer for effective
|
||||
* transfers.
|
||||
*
|
||||
* Instead of dynamically assign the FIFO buffer to channels, I chose
|
||||
* make one dedicated channel for memory-memory transfer. The
|
||||
* dedicated channel is public. Other channels are private and used
|
||||
* for slave transfer. Some devices in the SoC are wired to certain
|
||||
* DMA channel.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_MACH_TX49XX
|
||||
static inline bool txx9_dma_have_SMPCHN(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#define TXX9_DMA_USE_SIMPLE_CHAIN
|
||||
#else
|
||||
static inline bool txx9_dma_have_SMPCHN(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
#ifdef CONFIG_MACH_TX49XX
|
||||
#define CCR_LE TXX9_DMA_CCR_LE
|
||||
#define MCR_LE 0
|
||||
#else
|
||||
#define CCR_LE 0
|
||||
#define MCR_LE TXX9_DMA_MCR_LE
|
||||
#endif
|
||||
#else
|
||||
#define CCR_LE 0
|
||||
#define MCR_LE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Redefine this macro to handle differences between 32- and 64-bit
|
||||
* addressing, big vs. little endian, etc.
|
||||
*/
|
||||
#ifdef __BIG_ENDIAN
|
||||
#define TXX9_DMA_REG32(name) u32 __pad_##name; u32 name
|
||||
#else
|
||||
#define TXX9_DMA_REG32(name) u32 name; u32 __pad_##name
|
||||
#endif
|
||||
|
||||
/* Hardware register definitions. */
|
||||
struct txx9dmac_cregs {
|
||||
#if defined(CONFIG_32BIT) && !defined(CONFIG_64BIT_PHYS_ADDR)
|
||||
TXX9_DMA_REG32(CHAR); /* Chain Address Register */
|
||||
#else
|
||||
u64 CHAR; /* Chain Address Register */
|
||||
#endif
|
||||
u64 SAR; /* Source Address Register */
|
||||
u64 DAR; /* Destination Address Register */
|
||||
TXX9_DMA_REG32(CNTR); /* Count Register */
|
||||
TXX9_DMA_REG32(SAIR); /* Source Address Increment Register */
|
||||
TXX9_DMA_REG32(DAIR); /* Destination Address Increment Register */
|
||||
TXX9_DMA_REG32(CCR); /* Channel Control Register */
|
||||
TXX9_DMA_REG32(CSR); /* Channel Status Register */
|
||||
};
|
||||
struct txx9dmac_cregs32 {
|
||||
u32 CHAR;
|
||||
u32 SAR;
|
||||
u32 DAR;
|
||||
u32 CNTR;
|
||||
u32 SAIR;
|
||||
u32 DAIR;
|
||||
u32 CCR;
|
||||
u32 CSR;
|
||||
};
|
||||
|
||||
struct txx9dmac_regs {
|
||||
/* per-channel registers */
|
||||
struct txx9dmac_cregs CHAN[TXX9_DMA_MAX_NR_CHANNELS];
|
||||
u64 __pad[9];
|
||||
u64 MFDR; /* Memory Fill Data Register */
|
||||
TXX9_DMA_REG32(MCR); /* Master Control Register */
|
||||
};
|
||||
struct txx9dmac_regs32 {
|
||||
struct txx9dmac_cregs32 CHAN[TXX9_DMA_MAX_NR_CHANNELS];
|
||||
u32 __pad[9];
|
||||
u32 MFDR;
|
||||
u32 MCR;
|
||||
};
|
||||
|
||||
/* bits for MCR */
|
||||
#define TXX9_DMA_MCR_EIS(ch) (0x10000000<<(ch))
|
||||
#define TXX9_DMA_MCR_DIS(ch) (0x01000000<<(ch))
|
||||
#define TXX9_DMA_MCR_RSFIF 0x00000080
|
||||
#define TXX9_DMA_MCR_FIFUM(ch) (0x00000008<<(ch))
|
||||
#define TXX9_DMA_MCR_LE 0x00000004
|
||||
#define TXX9_DMA_MCR_RPRT 0x00000002
|
||||
#define TXX9_DMA_MCR_MSTEN 0x00000001
|
||||
|
||||
/* bits for CCRn */
|
||||
#define TXX9_DMA_CCR_IMMCHN 0x20000000
|
||||
#define TXX9_DMA_CCR_USEXFSZ 0x10000000
|
||||
#define TXX9_DMA_CCR_LE 0x08000000
|
||||
#define TXX9_DMA_CCR_DBINH 0x04000000
|
||||
#define TXX9_DMA_CCR_SBINH 0x02000000
|
||||
#define TXX9_DMA_CCR_CHRST 0x01000000
|
||||
#define TXX9_DMA_CCR_RVBYTE 0x00800000
|
||||
#define TXX9_DMA_CCR_ACKPOL 0x00400000
|
||||
#define TXX9_DMA_CCR_REQPL 0x00200000
|
||||
#define TXX9_DMA_CCR_EGREQ 0x00100000
|
||||
#define TXX9_DMA_CCR_CHDN 0x00080000
|
||||
#define TXX9_DMA_CCR_DNCTL 0x00060000
|
||||
#define TXX9_DMA_CCR_EXTRQ 0x00010000
|
||||
#define TXX9_DMA_CCR_INTRQD 0x0000e000
|
||||
#define TXX9_DMA_CCR_INTENE 0x00001000
|
||||
#define TXX9_DMA_CCR_INTENC 0x00000800
|
||||
#define TXX9_DMA_CCR_INTENT 0x00000400
|
||||
#define TXX9_DMA_CCR_CHNEN 0x00000200
|
||||
#define TXX9_DMA_CCR_XFACT 0x00000100
|
||||
#define TXX9_DMA_CCR_SMPCHN 0x00000020
|
||||
#define TXX9_DMA_CCR_XFSZ(order) (((order) << 2) & 0x0000001c)
|
||||
#define TXX9_DMA_CCR_XFSZ_1 TXX9_DMA_CCR_XFSZ(0)
|
||||
#define TXX9_DMA_CCR_XFSZ_2 TXX9_DMA_CCR_XFSZ(1)
|
||||
#define TXX9_DMA_CCR_XFSZ_4 TXX9_DMA_CCR_XFSZ(2)
|
||||
#define TXX9_DMA_CCR_XFSZ_8 TXX9_DMA_CCR_XFSZ(3)
|
||||
#define TXX9_DMA_CCR_XFSZ_X4 TXX9_DMA_CCR_XFSZ(4)
|
||||
#define TXX9_DMA_CCR_XFSZ_X8 TXX9_DMA_CCR_XFSZ(5)
|
||||
#define TXX9_DMA_CCR_XFSZ_X16 TXX9_DMA_CCR_XFSZ(6)
|
||||
#define TXX9_DMA_CCR_XFSZ_X32 TXX9_DMA_CCR_XFSZ(7)
|
||||
#define TXX9_DMA_CCR_MEMIO 0x00000002
|
||||
#define TXX9_DMA_CCR_SNGAD 0x00000001
|
||||
|
||||
/* bits for CSRn */
|
||||
#define TXX9_DMA_CSR_CHNEN 0x00000400
|
||||
#define TXX9_DMA_CSR_STLXFER 0x00000200
|
||||
#define TXX9_DMA_CSR_XFACT 0x00000100
|
||||
#define TXX9_DMA_CSR_ABCHC 0x00000080
|
||||
#define TXX9_DMA_CSR_NCHNC 0x00000040
|
||||
#define TXX9_DMA_CSR_NTRNFC 0x00000020
|
||||
#define TXX9_DMA_CSR_EXTDN 0x00000010
|
||||
#define TXX9_DMA_CSR_CFERR 0x00000008
|
||||
#define TXX9_DMA_CSR_CHERR 0x00000004
|
||||
#define TXX9_DMA_CSR_DESERR 0x00000002
|
||||
#define TXX9_DMA_CSR_SORERR 0x00000001
|
||||
|
||||
struct txx9dmac_chan {
|
||||
struct dma_chan chan;
|
||||
struct dma_device dma;
|
||||
struct txx9dmac_dev *ddev;
|
||||
void __iomem *ch_regs;
|
||||
struct tasklet_struct tasklet;
|
||||
int irq;
|
||||
u32 ccr;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
/* these other elements are all protected by lock */
|
||||
dma_cookie_t completed;
|
||||
struct list_head active_list;
|
||||
struct list_head queue;
|
||||
struct list_head free_list;
|
||||
|
||||
unsigned int descs_allocated;
|
||||
};
|
||||
|
||||
struct txx9dmac_dev {
|
||||
void __iomem *regs;
|
||||
struct tasklet_struct tasklet;
|
||||
int irq;
|
||||
struct txx9dmac_chan *chan[TXX9_DMA_MAX_NR_CHANNELS];
|
||||
bool have_64bit_regs;
|
||||
unsigned int descsize;
|
||||
};
|
||||
|
||||
static inline bool __is_dmac64(const struct txx9dmac_dev *ddev)
|
||||
{
|
||||
return ddev->have_64bit_regs;
|
||||
}
|
||||
|
||||
static inline bool is_dmac64(const struct txx9dmac_chan *dc)
|
||||
{
|
||||
return __is_dmac64(dc->ddev);
|
||||
}
|
||||
|
||||
#ifdef TXX9_DMA_USE_SIMPLE_CHAIN
|
||||
/* Hardware descriptor definition. (for simple-chain) */
|
||||
struct txx9dmac_hwdesc {
|
||||
#if defined(CONFIG_32BIT) && !defined(CONFIG_64BIT_PHYS_ADDR)
|
||||
TXX9_DMA_REG32(CHAR);
|
||||
#else
|
||||
u64 CHAR;
|
||||
#endif
|
||||
u64 SAR;
|
||||
u64 DAR;
|
||||
TXX9_DMA_REG32(CNTR);
|
||||
};
|
||||
struct txx9dmac_hwdesc32 {
|
||||
u32 CHAR;
|
||||
u32 SAR;
|
||||
u32 DAR;
|
||||
u32 CNTR;
|
||||
};
|
||||
#else
|
||||
#define txx9dmac_hwdesc txx9dmac_cregs
|
||||
#define txx9dmac_hwdesc32 txx9dmac_cregs32
|
||||
#endif
|
||||
|
||||
struct txx9dmac_desc {
|
||||
/* FIRST values the hardware uses */
|
||||
union {
|
||||
struct txx9dmac_hwdesc hwdesc;
|
||||
struct txx9dmac_hwdesc32 hwdesc32;
|
||||
};
|
||||
|
||||
/* THEN values for driver housekeeping */
|
||||
struct list_head desc_node ____cacheline_aligned;
|
||||
struct list_head tx_list;
|
||||
struct dma_async_tx_descriptor txd;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
#ifdef TXX9_DMA_USE_SIMPLE_CHAIN
|
||||
|
||||
static inline bool txx9dmac_chan_INTENT(struct txx9dmac_chan *dc)
|
||||
{
|
||||
return (dc->ccr & TXX9_DMA_CCR_INTENT) != 0;
|
||||
}
|
||||
|
||||
static inline void txx9dmac_chan_set_INTENT(struct txx9dmac_chan *dc)
|
||||
{
|
||||
dc->ccr |= TXX9_DMA_CCR_INTENT;
|
||||
}
|
||||
|
||||
static inline void txx9dmac_desc_set_INTENT(struct txx9dmac_dev *ddev,
|
||||
struct txx9dmac_desc *desc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void txx9dmac_chan_set_SMPCHN(struct txx9dmac_chan *dc)
|
||||
{
|
||||
dc->ccr |= TXX9_DMA_CCR_SMPCHN;
|
||||
}
|
||||
|
||||
static inline void txx9dmac_desc_set_nosimple(struct txx9dmac_dev *ddev,
|
||||
struct txx9dmac_desc *desc,
|
||||
u32 sair, u32 dair, u32 ccr)
|
||||
{
|
||||
}
|
||||
|
||||
#else /* TXX9_DMA_USE_SIMPLE_CHAIN */
|
||||
|
||||
static inline bool txx9dmac_chan_INTENT(struct txx9dmac_chan *dc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void txx9dmac_chan_set_INTENT(struct txx9dmac_chan *dc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void txx9dmac_desc_set_INTENT(struct txx9dmac_dev *ddev,
|
||||
struct txx9dmac_desc *desc)
|
||||
{
|
||||
if (__is_dmac64(ddev))
|
||||
desc->hwdesc.CCR |= TXX9_DMA_CCR_INTENT;
|
||||
else
|
||||
desc->hwdesc32.CCR |= TXX9_DMA_CCR_INTENT;
|
||||
}
|
||||
|
||||
static inline void txx9dmac_chan_set_SMPCHN(struct txx9dmac_chan *dc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void txx9dmac_desc_set_nosimple(struct txx9dmac_dev *ddev,
|
||||
struct txx9dmac_desc *desc,
|
||||
u32 sai, u32 dai, u32 ccr)
|
||||
{
|
||||
if (__is_dmac64(ddev)) {
|
||||
desc->hwdesc.SAIR = sai;
|
||||
desc->hwdesc.DAIR = dai;
|
||||
desc->hwdesc.CCR = ccr;
|
||||
} else {
|
||||
desc->hwdesc32.SAIR = sai;
|
||||
desc->hwdesc32.DAIR = dai;
|
||||
desc->hwdesc32.CCR = ccr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TXX9_DMA_USE_SIMPLE_CHAIN */
|
||||
|
||||
#endif /* TXX9DMAC_H */
|
Reference in New Issue
Block a user