Commit 4cb865de authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx: (33 commits)
  x86: poll waiting for I/OAT DMA channel status
  maintainers: add dma engine tree details
  dmaengine: add TODO items for future work on dma drivers
  dmaengine: Add API documentation for slave dma usage
  dmaengine/dw_dmac: Update maintainer-ship
  dmaengine: move link order
  dmaengine/dw_dmac: implement pause and resume in dwc_control
  dmaengine/dw_dmac: Replace spin_lock* with irqsave variants and enable submission from callback
  dmaengine/dw_dmac: Divide one sg to many desc, if sg len is greater than DWC_MAX_COUNT
  dmaengine/dw_dmac: set residue as total len in dwc_tx_status if status is !DMA_SUCCESS
  dmaengine/dw_dmac: don't call callback routine in case dmaengine_terminate_all() is called
  dmaengine: at_hdmac: pause: no need to wait for FIFO empty
  pch_dma: modify pci device table definition
  pch_dma: Support new device ML7223 IOH
  pch_dma: Support I2S for ML7213 IOH
  pch_dma: Fix DMA setting issue
  pch_dma: modify for checkpatch
  pch_dma: fix dma direction issue for ML7213 IOH video-in
  dmaengine: at_hdmac: use descriptor chaining help function
  dmaengine: at_hdmac: implement pause and resume in atc_control
  ...

Fix up trivial conflict in drivers/dma/dw_dmac.c
parents 55f08e1b 19d78a61
See Documentation/crypto/async-tx-api.txt DMA Engine API Guide
====================
Vinod Koul <vinod dot koul at intel.com>
NOTE: For DMA Engine usage in async_tx please see:
Documentation/crypto/async-tx-api.txt
Below is a guide to device driver writers on how to use the Slave-DMA API of the
DMA Engine. This is applicable only for slave DMA usage only.
The slave DMA usage consists of following steps
1. Allocate a DMA slave channel
2. Set slave and controller specific parameters
3. Get a descriptor for transaction
4. Submit the transaction and wait for callback notification
1. Allocate a DMA slave channel
Channel allocation is slightly different in the slave DMA context, client
drivers typically need a channel from a particular DMA controller only and even
in some cases a specific channel is desired. To request a channel
dma_request_channel() API is used.
Interface:
struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
dma_filter_fn filter_fn,
void *filter_param);
where dma_filter_fn is defined as:
typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
When the optional 'filter_fn' parameter is set to NULL dma_request_channel
simply returns the first channel that satisfies the capability mask. Otherwise,
when the mask parameter is insufficient for specifying the necessary channel,
the filter_fn routine can be used to disposition the available channels in the
system. The filter_fn routine is called once for each free channel in the
system. Upon seeing a suitable channel filter_fn returns DMA_ACK which flags
that channel to be the return value from dma_request_channel. A channel
allocated via this interface is exclusive to the caller, until
dma_release_channel() is called.
2. Set slave and controller specific parameters
Next step is always to pass some specific information to the DMA driver. Most of
the generic information which a slave DMA can use is in struct dma_slave_config.
It allows the clients to specify DMA direction, DMA addresses, bus widths, DMA
burst lengths etc. If some DMA controllers have more parameters to be sent then
they should try to embed struct dma_slave_config in their controller specific
structure. That gives flexibility to client to pass more parameters, if
required.
Interface:
int dmaengine_slave_config(struct dma_chan *chan,
struct dma_slave_config *config)
3. Get a descriptor for transaction
For slave usage the various modes of slave transfers supported by the
DMA-engine are:
slave_sg - DMA a list of scatter gather buffers from/to a peripheral
dma_cyclic - Perform a cyclic DMA operation from/to a peripheral till the
operation is explicitly stopped.
The non NULL return of this transfer API represents a "descriptor" for the given
transaction.
Interface:
struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_sg)(
struct dma_chan *chan,
struct scatterlist *dst_sg, unsigned int dst_nents,
struct scatterlist *src_sg, unsigned int src_nents,
unsigned long flags);
struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_data_direction direction);
4. Submit the transaction and wait for callback notification
To schedule the transaction to be scheduled by dma device, the "descriptor"
returned in above (3) needs to be submitted.
To tell the dma driver that a transaction is ready to be serviced, the
descriptor->submit() callback needs to be invoked. This chains the descriptor to
the pending queue.
The transactions in the pending queue can be activated by calling the
issue_pending API. If channel is idle then the first transaction in queue is
started and subsequent ones queued up.
On completion of the DMA operation the next in queue is submitted and a tasklet
triggered. The tasklet would then call the client driver completion callback
routine for notification, if set.
Interface:
void dma_async_issue_pending(struct dma_chan *chan);
==============================================================================
Additional usage notes for dma driver writers
1/ Although DMA engine specifies that completion callback routines cannot submit
any new operations, but typically for slave DMA subsequent transaction may not
be available for submit prior to callback routine being called. This requirement
is not a requirement for DMA-slave devices. But they should take care to drop
the spin-lock they might be holding before calling the callback routine
...@@ -2178,6 +2178,8 @@ M: Dan Williams <dan.j.williams@intel.com> ...@@ -2178,6 +2178,8 @@ M: Dan Williams <dan.j.williams@intel.com>
S: Supported S: Supported
F: drivers/dma/ F: drivers/dma/
F: include/linux/dma* F: include/linux/dma*
T: git git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx.git
T: git git://git.infradead.org/users/vkoul/slave-dma.git (slave-dma)
DME1737 HARDWARE MONITOR DRIVER DME1737 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com> M: Juerg Haefliger <juergh@gmail.com>
...@@ -5451,6 +5453,13 @@ L: linux-serial@vger.kernel.org ...@@ -5451,6 +5453,13 @@ L: linux-serial@vger.kernel.org
S: Maintained S: Maintained
F: drivers/tty/serial F: drivers/tty/serial
SYNOPSYS DESIGNWARE DMAC DRIVER
M: Viresh Kumar <viresh.kumar@st.com>
S: Maintained
F: include/linux/dw_dmac.h
F: drivers/dma/dw_dmac_regs.h
F: drivers/dma/dw_dmac.c
TIMEKEEPING, NTP TIMEKEEPING, NTP
M: John Stultz <johnstul@us.ibm.com> M: John Stultz <johnstul@us.ibm.com>
M: Thomas Gleixner <tglx@linutronix.de> M: Thomas Gleixner <tglx@linutronix.de>
......
...@@ -17,6 +17,9 @@ obj-$(CONFIG_SFI) += sfi/ ...@@ -17,6 +17,9 @@ obj-$(CONFIG_SFI) += sfi/
# was used and do nothing if so # was used and do nothing if so
obj-$(CONFIG_PNP) += pnp/ obj-$(CONFIG_PNP) += pnp/
obj-$(CONFIG_ARM_AMBA) += amba/ obj-$(CONFIG_ARM_AMBA) += amba/
# Many drivers will want to use DMA so this has to be made available
# really early.
obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_VIRTIO) += virtio/
obj-$(CONFIG_XEN) += xen/ obj-$(CONFIG_XEN) += xen/
...@@ -92,7 +95,6 @@ obj-$(CONFIG_EISA) += eisa/ ...@@ -92,7 +95,6 @@ obj-$(CONFIG_EISA) += eisa/
obj-y += lguest/ obj-y += lguest/
obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_MEMSTICK) += memstick/ obj-$(CONFIG_MEMSTICK) += memstick/
obj-y += leds/ obj-y += leds/
......
...@@ -200,16 +200,18 @@ config PL330_DMA ...@@ -200,16 +200,18 @@ config PL330_DMA
platform_data for a dma-pl330 device. platform_data for a dma-pl330 device.
config PCH_DMA config PCH_DMA
tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7213 IOH DMA support" tristate "Intel EG20T PCH / OKI Semi IOH(ML7213/ML7223) DMA support"
depends on PCI && X86 depends on PCI && X86
select DMA_ENGINE select DMA_ENGINE
help help
Enable support for 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/ This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
Output Hub) which is for IVI(In-Vehicle Infotainment) use. Output Hub), ML7213 and ML7223.
ML7213 is companion chip for Intel Atom E6xx series. ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is
ML7213 is completely compatible for Intel EG20T PCH. for MP(Media Phone) use.
ML7213/ML7223 is companion chip for Intel Atom E6xx series.
ML7213/ML7223 is completely compatible for Intel EG20T PCH.
config IMX_SDMA config IMX_SDMA
tristate "i.MX SDMA support" tristate "i.MX SDMA support"
......
TODO for slave dma
1. Move remaining drivers to use new slave interface
2. Remove old slave pointer machansim
3. Make issue_pending to start the transaction in below drivers
- mpc512x_dma
- imx-dma
- imx-sdma
- mxs-dma.c
- dw_dmac
- intel_mid_dma
- ste_dma40
4. Check other subsystems for dma drivers and merge/move to dmaengine
5. Remove dma_slave_config's dma direction.
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
#define ATC_DEFAULT_CFG (ATC_FIFOCFG_HALFFIFO) #define ATC_DEFAULT_CFG (ATC_FIFOCFG_HALFFIFO)
#define ATC_DEFAULT_CTRLA (0) #define ATC_DEFAULT_CTRLA (0)
#define ATC_DEFAULT_CTRLB (ATC_SIF(0) \ #define ATC_DEFAULT_CTRLB (ATC_SIF(AT_DMA_MEM_IF) \
|ATC_DIF(1)) |ATC_DIF(AT_DMA_MEM_IF))
/* /*
* Initial number of descriptors to allocate for each channel. This could * Initial number of descriptors to allocate for each channel. This could
...@@ -164,6 +164,29 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) ...@@ -164,6 +164,29 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
} }
} }
/**
* atc_desc_chain - build chain adding a descripor
* @first: address of first descripor of the chain
* @prev: address of previous descripor of the chain
* @desc: descriptor to queue
*
* Called from prep_* functions
*/
static void atc_desc_chain(struct at_desc **first, struct at_desc **prev,
struct at_desc *desc)
{
if (!(*first)) {
*first = desc;
} else {
/* inform the HW lli about chaining */
(*prev)->lli.dscr = desc->txd.phys;
/* insert the link descriptor to the LD ring */
list_add_tail(&desc->desc_node,
&(*first)->tx_list);
}
*prev = desc;
}
/** /**
* atc_assign_cookie - compute and assign new cookie * atc_assign_cookie - compute and assign new cookie
* @atchan: channel we work on * @atchan: channel we work on
...@@ -237,16 +260,12 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) ...@@ -237,16 +260,12 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first)
static void static void
atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
{ {
dma_async_tx_callback callback;
void *param;
struct dma_async_tx_descriptor *txd = &desc->txd; struct dma_async_tx_descriptor *txd = &desc->txd;
dev_vdbg(chan2dev(&atchan->chan_common), dev_vdbg(chan2dev(&atchan->chan_common),
"descriptor %u complete\n", txd->cookie); "descriptor %u complete\n", txd->cookie);
atchan->completed_cookie = txd->cookie; atchan->completed_cookie = txd->cookie;
callback = txd->callback;
param = txd->callback_param;
/* move children to free_list */ /* move children to free_list */
list_splice_init(&desc->tx_list, &atchan->free_list); list_splice_init(&desc->tx_list, &atchan->free_list);
...@@ -278,12 +297,19 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) ...@@ -278,12 +297,19 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
} }
} }
/* for cyclic transfers,
* no need to replay callback function while stopping */
if (!test_bit(ATC_IS_CYCLIC, &atchan->status)) {
dma_async_tx_callback callback = txd->callback;
void *param = txd->callback_param;
/* /*
* The API requires that no submissions are done from a * The API requires that no submissions are done from a
* callback, so we don't need to drop the lock here * callback, so we don't need to drop the lock here
*/ */
if (callback) if (callback)
callback(param); callback(param);
}
dma_run_dependencies(txd); dma_run_dependencies(txd);
} }
...@@ -419,6 +445,26 @@ static void atc_handle_error(struct at_dma_chan *atchan) ...@@ -419,6 +445,26 @@ static void atc_handle_error(struct at_dma_chan *atchan)
atc_chain_complete(atchan, bad_desc); atc_chain_complete(atchan, bad_desc);
} }
/**
* atc_handle_cyclic - at the end of a period, run callback function
* @atchan: channel used for cyclic operations
*
* Called with atchan->lock held and bh disabled
*/
static void atc_handle_cyclic(struct at_dma_chan *atchan)
{
struct at_desc *first = atc_first_active(atchan);
struct dma_async_tx_descriptor *txd = &first->txd;
dma_async_tx_callback callback = txd->callback;
void *param = txd->callback_param;
dev_vdbg(chan2dev(&atchan->chan_common),
"new cyclic period llp 0x%08x\n",
channel_readl(atchan, DSCR));
if (callback)
callback(param);
}
/*-- IRQ & Tasklet ---------------------------------------------------*/ /*-- IRQ & Tasklet ---------------------------------------------------*/
...@@ -426,16 +472,11 @@ static void atc_tasklet(unsigned long data) ...@@ -426,16 +472,11 @@ static void atc_tasklet(unsigned long data)
{ {
struct at_dma_chan *atchan = (struct at_dma_chan *)data; struct at_dma_chan *atchan = (struct at_dma_chan *)data;
/* Channel cannot be enabled here */
if (atc_chan_is_enabled(atchan)) {
dev_err(chan2dev(&atchan->chan_common),
"BUG: channel enabled in tasklet\n");
return;
}
spin_lock(&atchan->lock); spin_lock(&atchan->lock);
if (test_and_clear_bit(0, &atchan->error_status)) if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status))
atc_handle_error(atchan); atc_handle_error(atchan);
else if (test_bit(ATC_IS_CYCLIC, &atchan->status))
atc_handle_cyclic(atchan);
else else
atc_advance_work(atchan); atc_advance_work(atchan);
...@@ -464,12 +505,13 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id) ...@@ -464,12 +505,13 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
for (i = 0; i < atdma->dma_common.chancnt; i++) { for (i = 0; i < atdma->dma_common.chancnt; i++) {
atchan = &atdma->chan[i]; atchan = &atdma->chan[i];
if (pending & (AT_DMA_CBTC(i) | AT_DMA_ERR(i))) { if (pending & (AT_DMA_BTC(i) | AT_DMA_ERR(i))) {
if (pending & AT_DMA_ERR(i)) { if (pending & AT_DMA_ERR(i)) {
/* Disable channel on AHB error */ /* Disable channel on AHB error */
dma_writel(atdma, CHDR, atchan->mask); dma_writel(atdma, CHDR,
AT_DMA_RES(i) | atchan->mask);
/* Give information to tasklet */ /* Give information to tasklet */
set_bit(0, &atchan->error_status); set_bit(ATC_IS_ERROR, &atchan->status);
} }
tasklet_schedule(&atchan->tasklet); tasklet_schedule(&atchan->tasklet);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
...@@ -549,7 +591,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, ...@@ -549,7 +591,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
} }
ctrla = ATC_DEFAULT_CTRLA; ctrla = ATC_DEFAULT_CTRLA;
ctrlb = ATC_DEFAULT_CTRLB ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN
| ATC_SRC_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_INCR
| ATC_DST_ADDR_MODE_INCR | ATC_DST_ADDR_MODE_INCR
| ATC_FC_MEM2MEM; | ATC_FC_MEM2MEM;
...@@ -584,16 +626,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, ...@@ -584,16 +626,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
desc->txd.cookie = 0; desc->txd.cookie = 0;
if (!first) { atc_desc_chain(&first, &prev, desc);
first = desc;
} else {
/* inform the HW lli about chaining */
prev->lli.dscr = desc->txd.phys;
/* insert the link descriptor to the LD ring */
list_add_tail(&desc->desc_node,
&first->tx_list);
}
prev = desc;
} }
/* First descriptor of the chain embedds additional information */ /* First descriptor of the chain embedds additional information */
...@@ -639,7 +672,8 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -639,7 +672,8 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct scatterlist *sg; struct scatterlist *sg;
size_t total_len = 0; size_t total_len = 0;
dev_vdbg(chan2dev(chan), "prep_slave_sg: %s f0x%lx\n", dev_vdbg(chan2dev(chan), "prep_slave_sg (%d): %s f0x%lx\n",
sg_len,
direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE",
flags); flags);
...@@ -651,14 +685,15 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -651,14 +685,15 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
reg_width = atslave->reg_width; reg_width = atslave->reg_width;
ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla;
ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN; ctrlb = ATC_IEN;
switch (direction) { switch (direction) {
case DMA_TO_DEVICE: case DMA_TO_DEVICE:
ctrla |= ATC_DST_WIDTH(reg_width); ctrla |= ATC_DST_WIDTH(reg_width);
ctrlb |= ATC_DST_ADDR_MODE_FIXED ctrlb |= ATC_DST_ADDR_MODE_FIXED
| ATC_SRC_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_INCR
| ATC_FC_MEM2PER; | ATC_FC_MEM2PER
| ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF);
reg = atslave->tx_reg; reg = atslave->tx_reg;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
struct at_desc *desc; struct at_desc *desc;
...@@ -682,16 +717,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -682,16 +717,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
| len >> mem_width; | len >> mem_width;
desc->lli.ctrlb = ctrlb; desc->lli.ctrlb = ctrlb;
if (!first) { atc_desc_chain(&first, &prev, desc);
first = desc;
} else {
/* inform the HW lli about chaining */
prev->lli.dscr = desc->txd.phys;
/* insert the link descriptor to the LD ring */
list_add_tail(&desc->desc_node,
&first->tx_list);
}
prev = desc;
total_len += len; total_len += len;
} }
break; break;
...@@ -699,7 +725,8 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -699,7 +725,8 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
ctrla |= ATC_SRC_WIDTH(reg_width); ctrla |= ATC_SRC_WIDTH(reg_width);
ctrlb |= ATC_DST_ADDR_MODE_INCR ctrlb |= ATC_DST_ADDR_MODE_INCR
| ATC_SRC_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_FIXED
| ATC_FC_PER2MEM; | ATC_FC_PER2MEM
| ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF);
reg = atslave->rx_reg; reg = atslave->rx_reg;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
...@@ -724,16 +751,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -724,16 +751,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
| len >> reg_width; | len >> reg_width;
desc->lli.ctrlb = ctrlb; desc->lli.ctrlb = ctrlb;
if (!first) { atc_desc_chain(&first, &prev, desc);
first = desc;
} else {
/* inform the HW lli about chaining */
prev->lli.dscr = desc->txd.phys;
/* insert the link descriptor to the LD ring */
list_add_tail(&desc->desc_node,
&first->tx_list);
}
prev = desc;
total_len += len; total_len += len;
} }
break; break;
...@@ -759,18 +777,180 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -759,18 +777,180 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL; return NULL;
} }
/**
* atc_dma_cyclic_check_values
* Check for too big/unaligned periods and unaligned DMA buffer
*/
static int
atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr,
size_t period_len, enum dma_data_direction direction)
{
if (period_len > (ATC_BTSIZE_MAX << reg_width))
goto err_out;
if (unlikely(period_len & ((1 << reg_width) - 1)))
goto err_out;
if (unlikely(buf_addr & ((1 << reg_width) - 1)))
goto err_out;
if (unlikely(!(direction & (DMA_TO_DEVICE | DMA_FROM_DEVICE))))
goto err_out;
return 0;
err_out:
return -EINVAL;
}
/**
* atc_dma_cyclic_fill_desc - Fill one period decriptor
*/
static int
atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc,
unsigned int period_index, dma_addr_t buf_addr,
size_t period_len, enum dma_data_direction direction)
{
u32 ctrla;
unsigned int reg_width = atslave->reg_width;
/* prepare common CRTLA value */
ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla
| ATC_DST_WIDTH(reg_width)
| ATC_SRC_WIDTH(reg_width)
| period_len >> reg_width;
switch (direction) {
case DMA_TO_DEVICE:
desc->lli.saddr = buf_addr + (period_len * period_index);
desc->lli.daddr = atslave->tx_reg;
desc->lli.ctrla = ctrla;
desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED
| ATC_SRC_ADDR_MODE_INCR
| ATC_FC_MEM2PER
| ATC_SIF(AT_DMA_MEM_IF)
| ATC_DIF(AT_DMA_PER_IF);
break;
case DMA_FROM_DEVICE:
desc->lli.saddr = atslave->rx_reg;
desc->lli.daddr = buf_addr + (period_len * period_index);
desc->lli.ctrla = ctrla;
desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR
| ATC_SRC_ADDR_MODE_FIXED
| ATC_FC_PER2MEM
| ATC_SIF(AT_DMA_PER_IF)
| ATC_DIF(AT_DMA_MEM_IF);
break;
default:
return -EINVAL;
}
return 0;
}
/**
* atc_prep_dma_cyclic - prepare the cyclic DMA transfer
* @chan: the DMA channel to prepare
* @buf_addr: physical DMA address where the buffer starts
* @buf_len: total number of bytes for the entire buffer
* @period_len: number of bytes for each period
* @direction: transfer direction, to or from device
*/
static struct dma_async_tx_descriptor *
atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_data_direction direction)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma_slave *atslave = chan->private;
struct at_desc *first = NULL;
struct at_desc *prev = NULL;
unsigned long was_cyclic;
unsigned int periods = buf_len / period_len;
unsigned int i;
dev_vdbg(chan2dev(chan), "prep_dma_cyclic: %s buf@0x%08x - %d (%d/%d)\n",
direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE",
buf_addr,
periods, buf_len, period_len);
if (unlikely(!atslave || !buf_len || !period_len)) {
dev_dbg(chan2dev(chan), "prep_dma_cyclic: length is zero!\n");
return NULL;
}
was_cyclic = test_and_set_bit(ATC_IS_CYCLIC, &atchan->status);
if (was_cyclic) {
dev_dbg(chan2dev(chan), "prep_dma_cyclic: channel in use!\n");
return NULL;
}
/* Check for too big/unaligned periods and unaligned DMA buffer */
if (atc_dma_cyclic_check_values(atslave->reg_width, buf_addr,
period_len, direction))
goto err_out;
/* build cyclic linked list */
for (i = 0; i < periods; i++) {
struct at_desc *desc;
desc = atc_desc_get(atchan);
if (!desc)
goto err_desc_get;
if (atc_dma_cyclic_fill_desc(atslave, desc, i, buf_addr,
period_len, direction))
goto err_desc_get;
atc_desc_chain(&first, &prev, desc);
}
/* lets make a cyclic list */
prev->lli.dscr = first->txd.phys;
/* First descriptor of the chain embedds additional information */
first->txd.cookie = -EBUSY;
first->len = buf_len;
return &first->txd;
err_desc_get:
dev_err(chan2dev(chan), "not enough descriptors available\n");
atc_desc_put(atchan, first);
err_out:
clear_bit(ATC_IS_CYCLIC, &atchan->status);
return NULL;
}
static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg) unsigned long arg)
{ {
struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma *atdma = to_at_dma(chan->device); struct at_dma *atdma = to_at_dma(chan->device);
struct at_desc *desc, *_desc; int chan_id = atchan->chan_common.chan_id;
LIST_HEAD(list); LIST_HEAD(list);
/* Only supports DMA_TERMINATE_ALL */ dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd);
if (cmd != DMA_TERMINATE_ALL)
return -ENXIO; if (cmd == DMA_PAUSE) {
spin_lock_bh(&atchan->lock);
dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id));
set_bit(ATC_IS_PAUSED, &atchan->status);
spin_unlock_bh(&atchan->lock);
} else if (cmd == DMA_RESUME) {
if (!test_bit(ATC_IS_PAUSED, &atchan->status))
return 0;
spin_lock_bh(&atchan->lock);
dma_writel(atdma, CHDR, AT_DMA_RES(chan_id));
clear_bit(ATC_IS_PAUSED, &atchan->status);
spin_unlock_bh(&atchan->lock);
} else if (cmd == DMA_TERMINATE_ALL) {
struct at_desc *desc, *_desc;
/* /*
* This is only called when something went wrong elsewhere, so * This is only called when something went wrong elsewhere, so
* we don't really care about the data. Just disable the * we don't really care about the data. Just disable the
...@@ -779,7 +959,8 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -779,7 +959,8 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
*/ */
spin_lock_bh(&atchan->lock); spin_lock_bh(&atchan->lock);
dma_writel(atdma, CHDR, atchan->mask); /* disabling channel: must also remove suspend state */
dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask);
/* confirm that this channel is disabled */ /* confirm that this channel is disabled */
while (dma_readl(atdma, CHSR) & atchan->mask) while (dma_readl(atdma, CHSR) & atchan->mask)
...@@ -793,7 +974,14 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -793,7 +974,14 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
list_for_each_entry_safe(desc, _desc, &list, desc_node) list_for_each_entry_safe(desc, _desc, &list, desc_node)
atc_chain_complete(atchan, desc); atc_chain_complete(atchan, desc);
clear_bit(ATC_IS_PAUSED, &atchan->status);
/* if channel dedicated to cyclic operations, free it */
clear_bit(ATC_IS_CYCLIC, &atchan->status);
spin_unlock_bh(&atchan->lock); spin_unlock_bh(&atchan->lock);
} else {
return -ENXIO;
}
return 0; return 0;
} }
...@@ -835,9 +1023,17 @@ atc_tx_status(struct dma_chan *chan, ...@@ -835,9 +1023,17 @@ atc_tx_status(struct dma_chan *chan,
spin_unlock_bh(&atchan->lock); spin_unlock_bh(&atchan->lock);
if (ret != DMA_SUCCESS)
dma_set_tx_state(txstate, last_complete, last_used,
atc_first_active(atchan)->len);
else
dma_set_tx_state(txstate, last_complete, last_used, 0); dma_set_tx_state(txstate, last_complete, last_used, 0);
dev_vdbg(chan2dev(chan), "tx_status: %d (d%d, u%d)\n",
cookie, last_complete ? last_complete : 0, if (test_bit(ATC_IS_PAUSED, &atchan->status))
ret = DMA_PAUSED;
dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n",
ret, cookie, last_complete ? last_complete : 0,
last_used ? last_used : 0); last_used ? last_used : 0);
return ret; return ret;
...@@ -853,6 +1049,10 @@ static void atc_issue_pending(struct dma_chan *chan) ...@@ -853,6 +1049,10 @@ static void atc_issue_pending(struct dma_chan *chan)
dev_vdbg(chan2dev(chan), "issue_pending\n"); dev_vdbg(chan2dev(chan), "issue_pending\n");
/* Not needed for cyclic transfers */
if (test_bit(ATC_IS_CYCLIC, &atchan->status))
return;
spin_lock_bh(&atchan->lock); spin_lock_bh(&atchan->lock);
if (!atc_chan_is_enabled(atchan)) { if (!atc_chan_is_enabled(atchan)) {
atc_advance_work(atchan); atc_advance_work(atchan);
...@@ -959,6 +1159,7 @@ static void atc_free_chan_resources(struct dma_chan *chan) ...@@ -959,6 +1159,7 @@ static void atc_free_chan_resources(struct dma_chan *chan)
} }
list_splice_init(&atchan->free_list, &list); list_splice_init(&atchan->free_list, &list);
atchan->descs_allocated = 0; atchan->descs_allocated = 0;
atchan->status = 0;
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
} }
...@@ -1092,10 +1293,15 @@ static int __init at_dma_probe(struct platform_device *pdev) ...@@ -1092,10 +1293,15 @@ static int __init at_dma_probe(struct platform_device *pdev)
if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask)) if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask))
atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy; atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy;
if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) { if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask))
atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg;
if (dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask))
atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic;
if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ||
dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask))
atdma->dma_common.device_control = atc_control; atdma->dma_common.device_control = atc_control;
}
dma_writel(atdma, EN, AT_DMA_ENABLE); dma_writel(atdma, EN, AT_DMA_ENABLE);
......
...@@ -103,6 +103,10 @@ ...@@ -103,6 +103,10 @@
/* Bitfields in CTRLB */ /* Bitfields in CTRLB */
#define ATC_SIF(i) (0x3 & (i)) /* Src tx done via AHB-Lite Interface i */ #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_DIF(i) ((0x3 & (i)) << 4) /* Dst tx done via AHB-Lite Interface i */
/* Specify AHB interfaces */
#define AT_DMA_MEM_IF 0 /* interface 0 as memory interface */
#define AT_DMA_PER_IF 1 /* interface 1 as peripheral interface */
#define ATC_SRC_PIP (0x1 << 8) /* Source Picture-in-Picture enabled */ #define ATC_SRC_PIP (0x1 << 8) /* Source Picture-in-Picture enabled */
#define ATC_DST_PIP (0x1 << 12) /* Destination 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_SRC_DSCR_DIS (0x1 << 16) /* Src Descriptor fetch disable */
...@@ -180,13 +184,24 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd) ...@@ -180,13 +184,24 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd)
/*-- Channels --------------------------------------------------------*/ /*-- Channels --------------------------------------------------------*/
/**
* atc_status - information bits stored in channel status flag
*
* Manipulated with atomic operations.
*/
enum atc_status {
ATC_IS_ERROR = 0,
ATC_IS_PAUSED = 1,
ATC_IS_CYCLIC = 24,
};
/** /**
* struct at_dma_chan - internal representation of an Atmel HDMAC channel * struct at_dma_chan - internal representation of an Atmel HDMAC channel
* @chan_common: common dmaengine channel object members * @chan_common: common dmaengine channel object members
* @device: parent device * @device: parent device
* @ch_regs: memory mapped register base * @ch_regs: memory mapped register base
* @mask: channel index in a mask * @mask: channel index in a mask
* @error_status: transmit error status information from irq handler * @status: transmit status information from irq/prep* functions
* to tasklet (use atomic operations) * to tasklet (use atomic operations)
* @tasklet: bottom half to finish transaction work * @tasklet: bottom half to finish transaction work
* @lock: serializes enqueue/dequeue operations to descriptors lists * @lock: serializes enqueue/dequeue operations to descriptors lists
...@@ -201,7 +216,7 @@ struct at_dma_chan { ...@@ -201,7 +216,7 @@ struct at_dma_chan {
struct at_dma *device; struct at_dma *device;
void __iomem *ch_regs; void __iomem *ch_regs;
u8 mask; u8 mask;
unsigned long error_status; unsigned long status;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
spinlock_t lock; spinlock_t lock;
...@@ -309,8 +324,8 @@ static void atc_setup_irq(struct at_dma_chan *atchan, int on) ...@@ -309,8 +324,8 @@ static void atc_setup_irq(struct at_dma_chan *atchan, int on)
struct at_dma *atdma = to_at_dma(atchan->chan_common.device); struct at_dma *atdma = to_at_dma(atchan->chan_common.device);
u32 ebci; u32 ebci;
/* enable interrupts on buffer chain completion & error */ /* enable interrupts on buffer transfer completion & error */
ebci = AT_DMA_CBTC(atchan->chan_common.chan_id) ebci = AT_DMA_BTC(atchan->chan_common.chan_id)
| AT_DMA_ERR(atchan->chan_common.chan_id); | AT_DMA_ERR(atchan->chan_common.chan_id);
if (on) if (on)
dma_writel(atdma, EBCIER, ebci); dma_writel(atdma, EBCIER, ebci);
...@@ -347,7 +362,12 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan) ...@@ -347,7 +362,12 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan)
*/ */
static void set_desc_eol(struct at_desc *desc) static void set_desc_eol(struct at_desc *desc)
{ {
desc->lli.ctrlb |= ATC_SRC_DSCR_DIS | ATC_DST_DSCR_DIS; u32 ctrlb = desc->lli.ctrlb;
ctrlb &= ~ATC_IEN;
ctrlb |= ATC_SRC_DSCR_DIS | ATC_DST_DSCR_DIS;
desc->lli.ctrlb = ctrlb;
desc->lli.dscr = 0; desc->lli.dscr = 0;
} }
......
...@@ -1610,7 +1610,7 @@ int __init coh901318_init(void) ...@@ -1610,7 +1610,7 @@ int __init coh901318_init(void)
{ {
return platform_driver_probe(&coh901318_driver, coh901318_probe); return platform_driver_probe(&coh901318_driver, coh901318_probe);
} }
arch_initcall(coh901318_init); subsys_initcall(coh901318_init);
void __exit coh901318_exit(void) void __exit coh901318_exit(void)
{ {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* AVR32 systems.) * AVR32 systems.)
* *
* Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2007-2008 Atmel Corporation
* Copyright (C) 2010-2011 ST Microelectronics
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -93,8 +94,9 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) ...@@ -93,8 +94,9 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
struct dw_desc *desc, *_desc; struct dw_desc *desc, *_desc;
struct dw_desc *ret = NULL; struct dw_desc *ret = NULL;
unsigned int i = 0; unsigned int i = 0;
unsigned long flags;
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) {
if (async_tx_test_ack(&desc->txd)) { if (async_tx_test_ack(&desc->txd)) {
list_del(&desc->desc_node); list_del(&desc->desc_node);
...@@ -104,7 +106,7 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) ...@@ -104,7 +106,7 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc);
i++; i++;
} }
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i);
...@@ -130,12 +132,14 @@ static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) ...@@ -130,12 +132,14 @@ static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc)
*/ */
static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
{ {
unsigned long flags;
if (desc) { if (desc) {
struct dw_desc *child; struct dw_desc *child;
dwc_sync_desc_for_cpu(dwc, desc); dwc_sync_desc_for_cpu(dwc, desc);
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
list_for_each_entry(child, &desc->tx_list, desc_node) list_for_each_entry(child, &desc->tx_list, desc_node)
dev_vdbg(chan2dev(&dwc->chan), dev_vdbg(chan2dev(&dwc->chan),
"moving child desc %p to freelist\n", "moving child desc %p to freelist\n",
...@@ -143,7 +147,7 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) ...@@ -143,7 +147,7 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
list_splice_init(&desc->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); dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc);
list_add(&desc->desc_node, &dwc->free_list); list_add(&desc->desc_node, &dwc->free_list);
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
} }
} }
...@@ -195,18 +199,23 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) ...@@ -195,18 +199,23 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
static void static void
dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
bool callback_required)
{ {
dma_async_tx_callback callback; dma_async_tx_callback callback = NULL;
void *param; void *param = NULL;
struct dma_async_tx_descriptor *txd = &desc->txd; struct dma_async_tx_descriptor *txd = &desc->txd;
struct dw_desc *child; struct dw_desc *child;
unsigned long flags;
dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie);
spin_lock_irqsave(&dwc->lock, flags);
dwc->completed = txd->cookie; dwc->completed = txd->cookie;
if (callback_required) {
callback = txd->callback; callback = txd->callback;
param = txd->callback_param; param = txd->callback_param;
}
dwc_sync_desc_for_cpu(dwc, desc); dwc_sync_desc_for_cpu(dwc, desc);
...@@ -238,11 +247,9 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) ...@@ -238,11 +247,9 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc)
} }
} }
/* spin_unlock_irqrestore(&dwc->lock, flags);
* The API requires that no submissions are done from a
* callback, so we don't need to drop the lock here if (callback_required && callback)
*/
if (callback)
callback(param); callback(param);
} }
...@@ -250,7 +257,9 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -250,7 +257,9 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
{ {
struct dw_desc *desc, *_desc; struct dw_desc *desc, *_desc;
LIST_HEAD(list); LIST_HEAD(list);
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
if (dma_readl(dw, CH_EN) & dwc->mask) { if (dma_readl(dw, CH_EN) & dwc->mask) {
dev_err(chan2dev(&dwc->chan), dev_err(chan2dev(&dwc->chan),
"BUG: XFER bit set, but channel not idle!\n"); "BUG: XFER bit set, but channel not idle!\n");
...@@ -271,8 +280,10 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -271,8 +280,10 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
dwc_dostart(dwc, dwc_first_active(dwc)); dwc_dostart(dwc, dwc_first_active(dwc));
} }
spin_unlock_irqrestore(&dwc->lock, flags);
list_for_each_entry_safe(desc, _desc, &list, desc_node) list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc); dwc_descriptor_complete(dwc, desc, true);
} }
static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
...@@ -281,7 +292,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -281,7 +292,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
struct dw_desc *desc, *_desc; struct dw_desc *desc, *_desc;
struct dw_desc *child; struct dw_desc *child;
u32 status_xfer; u32 status_xfer;
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
/* /*
* Clear block interrupt flag before scanning so that we don't * Clear block interrupt flag before scanning so that we don't
* miss any, and read LLP before RAW_XFER to ensure it is * miss any, and read LLP before RAW_XFER to ensure it is
...@@ -294,30 +307,47 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -294,30 +307,47 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
if (status_xfer & dwc->mask) { if (status_xfer & dwc->mask) {
/* Everything we've submitted is done */ /* Everything we've submitted is done */
dma_writel(dw, CLEAR.XFER, dwc->mask); dma_writel(dw, CLEAR.XFER, dwc->mask);
spin_unlock_irqrestore(&dwc->lock, flags);
dwc_complete_all(dw, dwc); dwc_complete_all(dw, dwc);
return; return;
} }
if (list_empty(&dwc->active_list)) if (list_empty(&dwc->active_list)) {
spin_unlock_irqrestore(&dwc->lock, flags);
return; return;
}
dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%x\n", llp); dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%x\n", llp);
list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
if (desc->lli.llp == llp) /* check first descriptors addr */
if (desc->txd.phys == llp) {
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
/* check first descriptors llp */
if (desc->lli.llp == llp) {
/* This one is currently in progress */ /* This one is currently in progress */
spin_unlock_irqrestore(&dwc->lock, flags);
return; return;
}
list_for_each_entry(child, &desc->tx_list, desc_node) list_for_each_entry(child, &desc->tx_list, desc_node)
if (child->lli.llp == llp) if (child->lli.llp == llp) {
/* Currently in progress */ /* Currently in progress */
spin_unlock_irqrestore(&dwc->lock, flags);
return; return;
}
/* /*
* No descriptors so far seem to be in progress, i.e. * No descriptors so far seem to be in progress, i.e.
* this one must be done. * this one must be done.
*/ */
dwc_descriptor_complete(dwc, desc); spin_unlock_irqrestore(&dwc->lock, flags);
dwc_descriptor_complete(dwc, desc, true);
spin_lock_irqsave(&dwc->lock, flags);
} }
dev_err(chan2dev(&dwc->chan), dev_err(chan2dev(&dwc->chan),
...@@ -332,6 +362,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -332,6 +362,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
list_move(dwc->queue.next, &dwc->active_list); list_move(dwc->queue.next, &dwc->active_list);
dwc_dostart(dwc, dwc_first_active(dwc)); dwc_dostart(dwc, dwc_first_active(dwc));
} }
spin_unlock_irqrestore(&dwc->lock, flags);
} }
static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli)
...@@ -346,9 +377,12 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -346,9 +377,12 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
{ {
struct dw_desc *bad_desc; struct dw_desc *bad_desc;
struct dw_desc *child; struct dw_desc *child;
unsigned long flags;
dwc_scan_descriptors(dw, dwc); dwc_scan_descriptors(dw, dwc);
spin_lock_irqsave(&dwc->lock, flags);
/* /*
* The descriptor currently at the head of the active list is * The descriptor currently at the head of the active list is
* borked. Since we don't have any way to report errors, we'll * borked. Since we don't have any way to report errors, we'll
...@@ -378,8 +412,10 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) ...@@ -378,8 +412,10 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
list_for_each_entry(child, &bad_desc->tx_list, desc_node) list_for_each_entry(child, &bad_desc->tx_list, desc_node)
dwc_dump_lli(dwc, &child->lli); dwc_dump_lli(dwc, &child->lli);
spin_unlock_irqrestore(&dwc->lock, flags);
/* Pretend the descriptor completed successfully */ /* Pretend the descriptor completed successfully */
dwc_descriptor_complete(dwc, bad_desc); dwc_descriptor_complete(dwc, bad_desc, true);
} }
/* --------------------- Cyclic DMA API extensions -------------------- */ /* --------------------- Cyclic DMA API extensions -------------------- */
...@@ -402,6 +438,8 @@ EXPORT_SYMBOL(dw_dma_get_dst_addr); ...@@ -402,6 +438,8 @@ EXPORT_SYMBOL(dw_dma_get_dst_addr);
static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
u32 status_block, u32 status_err, u32 status_xfer) u32 status_block, u32 status_err, u32 status_xfer)
{ {
unsigned long flags;
if (status_block & dwc->mask) { if (status_block & dwc->mask) {
void (*callback)(void *param); void (*callback)(void *param);
void *callback_param; void *callback_param;
...@@ -412,11 +450,9 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, ...@@ -412,11 +450,9 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
callback = dwc->cdesc->period_callback; callback = dwc->cdesc->period_callback;
callback_param = dwc->cdesc->period_callback_param; callback_param = dwc->cdesc->period_callback_param;
if (callback) {
spin_unlock(&dwc->lock); if (callback)
callback(callback_param); callback(callback_param);
spin_lock(&dwc->lock);
}
} }
/* /*
...@@ -430,6 +466,9 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, ...@@ -430,6 +466,9 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s " dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s "
"interrupt, stopping DMA transfer\n", "interrupt, stopping DMA transfer\n",
status_xfer ? "xfer" : "error"); status_xfer ? "xfer" : "error");
spin_lock_irqsave(&dwc->lock, flags);
dev_err(chan2dev(&dwc->chan), dev_err(chan2dev(&dwc->chan),
" SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n",
channel_readl(dwc, SAR), channel_readl(dwc, SAR),
...@@ -453,6 +492,8 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, ...@@ -453,6 +492,8 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
for (i = 0; i < dwc->cdesc->periods; i++) for (i = 0; i < dwc->cdesc->periods; i++)
dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli);
spin_unlock_irqrestore(&dwc->lock, flags);
} }
} }
...@@ -476,7 +517,6 @@ static void dw_dma_tasklet(unsigned long data) ...@@ -476,7 +517,6 @@ static void dw_dma_tasklet(unsigned long data)
for (i = 0; i < dw->dma.chancnt; i++) { for (i = 0; i < dw->dma.chancnt; i++) {
dwc = &dw->chan[i]; dwc = &dw->chan[i];
spin_lock(&dwc->lock);
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags))
dwc_handle_cyclic(dw, dwc, status_block, status_err, dwc_handle_cyclic(dw, dwc, status_block, status_err,
status_xfer); status_xfer);
...@@ -484,7 +524,6 @@ static void dw_dma_tasklet(unsigned long data) ...@@ -484,7 +524,6 @@ static void dw_dma_tasklet(unsigned long data)
dwc_handle_error(dw, dwc); dwc_handle_error(dw, dwc);
else if ((status_block | status_xfer) & (1 << i)) else if ((status_block | status_xfer) & (1 << i))
dwc_scan_descriptors(dw, dwc); dwc_scan_descriptors(dw, dwc);
spin_unlock(&dwc->lock);
} }
/* /*
...@@ -539,8 +578,9 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) ...@@ -539,8 +578,9 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
struct dw_desc *desc = txd_to_dw_desc(tx); struct dw_desc *desc = txd_to_dw_desc(tx);
struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan);
dma_cookie_t cookie; dma_cookie_t cookie;
unsigned long flags;
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
cookie = dwc_assign_cookie(dwc, desc); cookie = dwc_assign_cookie(dwc, desc);
/* /*
...@@ -560,7 +600,7 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) ...@@ -560,7 +600,7 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
list_add_tail(&desc->desc_node, &dwc->queue); list_add_tail(&desc->desc_node, &dwc->queue);
} }
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
return cookie; return cookie;
} }
...@@ -689,9 +729,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -689,9 +729,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
reg = dws->tx_reg; reg = dws->tx_reg;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc; struct dw_desc *desc;
u32 len; u32 len, dlen, mem;
u32 mem;
mem = sg_phys(sg);
len = sg_dma_len(sg);
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
slave_sg_todev_fill_desc:
desc = dwc_desc_get(dwc); desc = dwc_desc_get(dwc);
if (!desc) { if (!desc) {
dev_err(chan2dev(chan), dev_err(chan2dev(chan),
...@@ -699,16 +745,19 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -699,16 +745,19 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
goto err_desc_get; goto err_desc_get;
} }
mem = sg_phys(sg);
len = sg_dma_len(sg);
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
desc->lli.sar = mem; desc->lli.sar = mem;
desc->lli.dar = reg; desc->lli.dar = reg;
desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width);
desc->lli.ctlhi = len >> mem_width; if ((len >> mem_width) > DWC_MAX_COUNT) {
dlen = DWC_MAX_COUNT << mem_width;
mem += dlen;
len -= dlen;
} else {
dlen = len;
len = 0;
}
desc->lli.ctlhi = dlen >> mem_width;
if (!first) { if (!first) {
first = desc; first = desc;
...@@ -722,7 +771,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -722,7 +771,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
&first->tx_list); &first->tx_list);
} }
prev = desc; prev = desc;
total_len += len; total_len += dlen;
if (len)
goto slave_sg_todev_fill_desc;
} }
break; break;
case DMA_FROM_DEVICE: case DMA_FROM_DEVICE:
...@@ -735,9 +787,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -735,9 +787,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
reg = dws->rx_reg; reg = dws->rx_reg;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc; struct dw_desc *desc;
u32 len; u32 len, dlen, mem;
u32 mem;
mem = sg_phys(sg);
len = sg_dma_len(sg);
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
slave_sg_fromdev_fill_desc:
desc = dwc_desc_get(dwc); desc = dwc_desc_get(dwc);
if (!desc) { if (!desc) {
dev_err(chan2dev(chan), dev_err(chan2dev(chan),
...@@ -745,16 +803,18 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -745,16 +803,18 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
goto err_desc_get; goto err_desc_get;
} }
mem = sg_phys(sg);
len = sg_dma_len(sg);
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
desc->lli.sar = reg; desc->lli.sar = reg;
desc->lli.dar = mem; desc->lli.dar = mem;
desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width);
desc->lli.ctlhi = len >> reg_width; if ((len >> reg_width) > DWC_MAX_COUNT) {
dlen = DWC_MAX_COUNT << reg_width;
mem += dlen;
len -= dlen;
} else {
dlen = len;
len = 0;
}
desc->lli.ctlhi = dlen >> reg_width;
if (!first) { if (!first) {
first = desc; first = desc;
...@@ -768,7 +828,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -768,7 +828,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
&first->tx_list); &first->tx_list);
} }
prev = desc; prev = desc;
total_len += len; total_len += dlen;
if (len)
goto slave_sg_fromdev_fill_desc;
} }
break; break;
default: default:
...@@ -799,34 +862,51 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -799,34 +862,51 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device); struct dw_dma *dw = to_dw_dma(chan->device);
struct dw_desc *desc, *_desc; struct dw_desc *desc, *_desc;
unsigned long flags;
u32 cfglo;
LIST_HEAD(list); LIST_HEAD(list);
/* Only supports DMA_TERMINATE_ALL */ if (cmd == DMA_PAUSE) {
if (cmd != DMA_TERMINATE_ALL) spin_lock_irqsave(&dwc->lock, flags);
return -ENXIO;
/* cfglo = channel_readl(dwc, CFG_LO);
* This is only called when something went wrong elsewhere, so channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
* we don't really care about the data. Just disable the while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY))
* channel. We still have to poll the channel enable bit due cpu_relax();
* to AHB/HSB limitations.
*/
spin_lock_bh(&dwc->lock);
channel_clear_bit(dw, CH_EN, dwc->mask); dwc->paused = true;
spin_unlock_irqrestore(&dwc->lock, flags);
} else if (cmd == DMA_RESUME) {
if (!dwc->paused)
return 0;
spin_lock_irqsave(&dwc->lock, flags);
cfglo = channel_readl(dwc, CFG_LO);
channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
dwc->paused = false;
spin_unlock_irqrestore(&dwc->lock, flags);
} else if (cmd == DMA_TERMINATE_ALL) {
spin_lock_irqsave(&dwc->lock, flags);
channel_clear_bit(dw, CH_EN, dwc->mask);
while (dma_readl(dw, CH_EN) & dwc->mask) while (dma_readl(dw, CH_EN) & dwc->mask)
cpu_relax(); cpu_relax();
dwc->paused = false;
/* active_list entries will end up before queued entries */ /* active_list entries will end up before queued entries */
list_splice_init(&dwc->queue, &list); list_splice_init(&dwc->queue, &list);
list_splice_init(&dwc->active_list, &list); list_splice_init(&dwc->active_list, &list);
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
/* Flush all pending and queued descriptors */ /* Flush all pending and queued descriptors */
list_for_each_entry_safe(desc, _desc, &list, desc_node) list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc); dwc_descriptor_complete(dwc, desc, false);
} else
return -ENXIO;
return 0; return 0;
} }
...@@ -846,9 +926,7 @@ dwc_tx_status(struct dma_chan *chan, ...@@ -846,9 +926,7 @@ dwc_tx_status(struct dma_chan *chan,
ret = dma_async_is_complete(cookie, last_complete, last_used); ret = dma_async_is_complete(cookie, last_complete, last_used);
if (ret != DMA_SUCCESS) { if (ret != DMA_SUCCESS) {
spin_lock_bh(&dwc->lock);
dwc_scan_descriptors(to_dw_dma(chan->device), dwc); dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
spin_unlock_bh(&dwc->lock);
last_complete = dwc->completed; last_complete = dwc->completed;
last_used = chan->cookie; last_used = chan->cookie;
...@@ -856,8 +934,15 @@ dwc_tx_status(struct dma_chan *chan, ...@@ -856,8 +934,15 @@ dwc_tx_status(struct dma_chan *chan,
ret = dma_async_is_complete(cookie, last_complete, last_used); ret = dma_async_is_complete(cookie, last_complete, last_used);
} }
if (ret != DMA_SUCCESS)
dma_set_tx_state(txstate, last_complete, last_used,
dwc_first_active(dwc)->len);
else
dma_set_tx_state(txstate, last_complete, last_used, 0); dma_set_tx_state(txstate, last_complete, last_used, 0);
if (dwc->paused)
return DMA_PAUSED;
return ret; return ret;
} }
...@@ -865,10 +950,8 @@ static void dwc_issue_pending(struct dma_chan *chan) ...@@ -865,10 +950,8 @@ static void dwc_issue_pending(struct dma_chan *chan)
{ {
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
spin_lock_bh(&dwc->lock);
if (!list_empty(&dwc->queue)) if (!list_empty(&dwc->queue))
dwc_scan_descriptors(to_dw_dma(chan->device), dwc); dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
spin_unlock_bh(&dwc->lock);
} }
static int dwc_alloc_chan_resources(struct dma_chan *chan) static int dwc_alloc_chan_resources(struct dma_chan *chan)
...@@ -880,6 +963,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) ...@@ -880,6 +963,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
int i; int i;
u32 cfghi; u32 cfghi;
u32 cfglo; u32 cfglo;
unsigned long flags;
dev_vdbg(chan2dev(chan), "alloc_chan_resources\n"); dev_vdbg(chan2dev(chan), "alloc_chan_resources\n");
...@@ -917,16 +1001,16 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) ...@@ -917,16 +1001,16 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
* doesn't mean what you think it means), and status writeback. * doesn't mean what you think it means), and status writeback.
*/ */
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
i = dwc->descs_allocated; i = dwc->descs_allocated;
while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) {
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL);
if (!desc) { if (!desc) {
dev_info(chan2dev(chan), dev_info(chan2dev(chan),
"only allocated %d descriptors\n", i); "only allocated %d descriptors\n", i);
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
break; break;
} }
...@@ -938,7 +1022,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) ...@@ -938,7 +1022,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
sizeof(desc->lli), DMA_TO_DEVICE); sizeof(desc->lli), DMA_TO_DEVICE);
dwc_desc_put(dwc, desc); dwc_desc_put(dwc, desc);
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
i = ++dwc->descs_allocated; i = ++dwc->descs_allocated;
} }
...@@ -947,7 +1031,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) ...@@ -947,7 +1031,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
channel_set_bit(dw, MASK.BLOCK, dwc->mask); channel_set_bit(dw, MASK.BLOCK, dwc->mask);
channel_set_bit(dw, MASK.ERROR, dwc->mask); channel_set_bit(dw, MASK.ERROR, dwc->mask);
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
dev_dbg(chan2dev(chan), dev_dbg(chan2dev(chan),
"alloc_chan_resources allocated %d descriptors\n", i); "alloc_chan_resources allocated %d descriptors\n", i);
...@@ -960,6 +1044,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) ...@@ -960,6 +1044,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device); struct dw_dma *dw = to_dw_dma(chan->device);
struct dw_desc *desc, *_desc; struct dw_desc *desc, *_desc;
unsigned long flags;
LIST_HEAD(list); LIST_HEAD(list);
dev_dbg(chan2dev(chan), "free_chan_resources (descs allocated=%u)\n", dev_dbg(chan2dev(chan), "free_chan_resources (descs allocated=%u)\n",
...@@ -970,7 +1055,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) ...@@ -970,7 +1055,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
BUG_ON(!list_empty(&dwc->queue)); BUG_ON(!list_empty(&dwc->queue));
BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask);
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
list_splice_init(&dwc->free_list, &list); list_splice_init(&dwc->free_list, &list);
dwc->descs_allocated = 0; dwc->descs_allocated = 0;
...@@ -979,7 +1064,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) ...@@ -979,7 +1064,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
channel_clear_bit(dw, MASK.BLOCK, dwc->mask); channel_clear_bit(dw, MASK.BLOCK, dwc->mask);
channel_clear_bit(dw, MASK.ERROR, dwc->mask); channel_clear_bit(dw, MASK.ERROR, dwc->mask);
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
list_for_each_entry_safe(desc, _desc, &list, desc_node) { list_for_each_entry_safe(desc, _desc, &list, desc_node) {
dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc);
...@@ -1004,13 +1089,14 @@ int dw_dma_cyclic_start(struct dma_chan *chan) ...@@ -1004,13 +1089,14 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
{ {
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(dwc->chan.device); struct dw_dma *dw = to_dw_dma(dwc->chan.device);
unsigned long flags;
if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) {
dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n");
return -ENODEV; return -ENODEV;
} }
spin_lock(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
/* assert channel is idle */ /* assert channel is idle */
if (dma_readl(dw, CH_EN) & dwc->mask) { if (dma_readl(dw, CH_EN) & dwc->mask) {
...@@ -1023,7 +1109,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan) ...@@ -1023,7 +1109,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
channel_readl(dwc, LLP), channel_readl(dwc, LLP),
channel_readl(dwc, CTL_HI), channel_readl(dwc, CTL_HI),
channel_readl(dwc, CTL_LO)); channel_readl(dwc, CTL_LO));
spin_unlock(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
return -EBUSY; return -EBUSY;
} }
...@@ -1038,7 +1124,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan) ...@@ -1038,7 +1124,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
channel_set_bit(dw, CH_EN, dwc->mask); channel_set_bit(dw, CH_EN, dwc->mask);
spin_unlock(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
return 0; return 0;
} }
...@@ -1054,14 +1140,15 @@ void dw_dma_cyclic_stop(struct dma_chan *chan) ...@@ -1054,14 +1140,15 @@ void dw_dma_cyclic_stop(struct dma_chan *chan)
{ {
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(dwc->chan.device); struct dw_dma *dw = to_dw_dma(dwc->chan.device);
unsigned long flags;
spin_lock(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
channel_clear_bit(dw, CH_EN, dwc->mask); channel_clear_bit(dw, CH_EN, dwc->mask);
while (dma_readl(dw, CH_EN) & dwc->mask) while (dma_readl(dw, CH_EN) & dwc->mask)
cpu_relax(); cpu_relax();
spin_unlock(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
} }
EXPORT_SYMBOL(dw_dma_cyclic_stop); EXPORT_SYMBOL(dw_dma_cyclic_stop);
...@@ -1090,17 +1177,18 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, ...@@ -1090,17 +1177,18 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
unsigned int reg_width; unsigned int reg_width;
unsigned int periods; unsigned int periods;
unsigned int i; unsigned int i;
unsigned long flags;
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) {
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
dev_dbg(chan2dev(&dwc->chan), dev_dbg(chan2dev(&dwc->chan),
"queue and/or active list are not empty\n"); "queue and/or active list are not empty\n");
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
} }
was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags);
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
if (was_cyclic) { if (was_cyclic) {
dev_dbg(chan2dev(&dwc->chan), dev_dbg(chan2dev(&dwc->chan),
"channel already prepared for cyclic DMA\n"); "channel already prepared for cyclic DMA\n");
...@@ -1214,13 +1302,14 @@ void dw_dma_cyclic_free(struct dma_chan *chan) ...@@ -1214,13 +1302,14 @@ void dw_dma_cyclic_free(struct dma_chan *chan)
struct dw_dma *dw = to_dw_dma(dwc->chan.device); struct dw_dma *dw = to_dw_dma(dwc->chan.device);
struct dw_cyclic_desc *cdesc = dwc->cdesc; struct dw_cyclic_desc *cdesc = dwc->cdesc;
int i; int i;
unsigned long flags;
dev_dbg(chan2dev(&dwc->chan), "cyclic free\n"); dev_dbg(chan2dev(&dwc->chan), "cyclic free\n");
if (!cdesc) if (!cdesc)
return; return;
spin_lock_bh(&dwc->lock); spin_lock_irqsave(&dwc->lock, flags);
channel_clear_bit(dw, CH_EN, dwc->mask); channel_clear_bit(dw, CH_EN, dwc->mask);
while (dma_readl(dw, CH_EN) & dwc->mask) while (dma_readl(dw, CH_EN) & dwc->mask)
...@@ -1230,7 +1319,7 @@ void dw_dma_cyclic_free(struct dma_chan *chan) ...@@ -1230,7 +1319,7 @@ void dw_dma_cyclic_free(struct dma_chan *chan)
dma_writel(dw, CLEAR.ERROR, dwc->mask); dma_writel(dw, CLEAR.ERROR, dwc->mask);
dma_writel(dw, CLEAR.XFER, dwc->mask); dma_writel(dw, CLEAR.XFER, dwc->mask);
spin_unlock_bh(&dwc->lock); spin_unlock_irqrestore(&dwc->lock, flags);
for (i = 0; i < cdesc->periods; i++) for (i = 0; i < cdesc->periods; i++)
dwc_desc_put(dwc, cdesc->desc[i]); dwc_desc_put(dwc, cdesc->desc[i]);
...@@ -1487,3 +1576,4 @@ module_exit(dw_exit); ...@@ -1487,3 +1576,4 @@ module_exit(dw_exit);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver");
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Driver for the Synopsys DesignWare AHB DMA Controller * Driver for the Synopsys DesignWare AHB DMA Controller
* *
* Copyright (C) 2005-2007 Atmel Corporation * Copyright (C) 2005-2007 Atmel Corporation
* Copyright (C) 2010-2011 ST Microelectronics
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -138,6 +139,7 @@ struct dw_dma_chan { ...@@ -138,6 +139,7 @@ struct dw_dma_chan {
void __iomem *ch_regs; void __iomem *ch_regs;
u8 mask; u8 mask;
u8 priority; u8 priority;
bool paused;
spinlock_t lock; spinlock_t lock;
......
...@@ -1292,8 +1292,7 @@ static int __devinit intel_mid_dma_probe(struct pci_dev *pdev, ...@@ -1292,8 +1292,7 @@ static int __devinit intel_mid_dma_probe(struct pci_dev *pdev,
if (err) if (err)
goto err_dma; goto err_dma;
pm_runtime_set_active(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_allow(&pdev->dev); pm_runtime_allow(&pdev->dev);
return 0; return 0;
...@@ -1322,6 +1321,9 @@ static int __devinit intel_mid_dma_probe(struct pci_dev *pdev, ...@@ -1322,6 +1321,9 @@ static int __devinit intel_mid_dma_probe(struct pci_dev *pdev,
static void __devexit intel_mid_dma_remove(struct pci_dev *pdev) static void __devexit intel_mid_dma_remove(struct pci_dev *pdev)
{ {
struct middma_device *device = pci_get_drvdata(pdev); struct middma_device *device = pci_get_drvdata(pdev);
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_forbid(&pdev->dev);
middma_shutdown(pdev); middma_shutdown(pdev);
pci_dev_put(pdev); pci_dev_put(pdev);
kfree(device); kfree(device);
...@@ -1385,13 +1387,20 @@ int dma_resume(struct pci_dev *pci) ...@@ -1385,13 +1387,20 @@ int dma_resume(struct pci_dev *pci)
static int dma_runtime_suspend(struct device *dev) static int dma_runtime_suspend(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
return dma_suspend(pci_dev, PMSG_SUSPEND); struct middma_device *device = pci_get_drvdata(pci_dev);
device->state = SUSPENDED;
return 0;
} }
static int dma_runtime_resume(struct device *dev) static int dma_runtime_resume(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
return dma_resume(pci_dev); struct middma_device *device = pci_get_drvdata(pci_dev);
device->state = RUNNING;
iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
return 0;
} }
static int dma_runtime_idle(struct device *dev) static int dma_runtime_idle(struct device *dev)
......
...@@ -508,6 +508,7 @@ int ioat2_alloc_chan_resources(struct dma_chan *c) ...@@ -508,6 +508,7 @@ int ioat2_alloc_chan_resources(struct dma_chan *c)
struct ioat_ring_ent **ring; struct ioat_ring_ent **ring;
u64 status; u64 status;
int order; int order;
int i = 0;
/* have we already been set up? */ /* have we already been set up? */
if (ioat->ring) if (ioat->ring)
...@@ -548,8 +549,11 @@ int ioat2_alloc_chan_resources(struct dma_chan *c) ...@@ -548,8 +549,11 @@ int ioat2_alloc_chan_resources(struct dma_chan *c)
ioat2_start_null_desc(ioat); ioat2_start_null_desc(ioat);
/* check that we got off the ground */ /* check that we got off the ground */
udelay(5); do {
udelay(1);
status = ioat_chansts(chan); status = ioat_chansts(chan);
} while (i++ < 20 && !is_ioat_active(status) && !is_ioat_idle(status));
if (is_ioat_active(status) || is_ioat_idle(status)) { if (is_ioat_active(status) || is_ioat_idle(status)) {
set_bit(IOAT_RUN, &chan->state); set_bit(IOAT_RUN, &chan->state);
return 1 << ioat->alloc_order; return 1 << ioat->alloc_order;
......
...@@ -619,7 +619,7 @@ iop_adma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest, ...@@ -619,7 +619,7 @@ iop_adma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest,
if (unlikely(!len)) if (unlikely(!len))
return NULL; return NULL;
BUG_ON(unlikely(len > IOP_ADMA_MAX_BYTE_COUNT)); BUG_ON(len > IOP_ADMA_MAX_BYTE_COUNT);
dev_dbg(iop_chan->device->common.dev, "%s len: %u\n", dev_dbg(iop_chan->device->common.dev, "%s len: %u\n",
__func__, len); __func__, len);
...@@ -652,7 +652,7 @@ iop_adma_prep_dma_memset(struct dma_chan *chan, dma_addr_t dma_dest, ...@@ -652,7 +652,7 @@ iop_adma_prep_dma_memset(struct dma_chan *chan, dma_addr_t dma_dest,
if (unlikely(!len)) if (unlikely(!len))
return NULL; return NULL;
BUG_ON(unlikely(len > IOP_ADMA_MAX_BYTE_COUNT)); BUG_ON(len > IOP_ADMA_MAX_BYTE_COUNT);
dev_dbg(iop_chan->device->common.dev, "%s len: %u\n", dev_dbg(iop_chan->device->common.dev, "%s len: %u\n",
__func__, len); __func__, len);
...@@ -686,7 +686,7 @@ iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest, ...@@ -686,7 +686,7 @@ iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest,
if (unlikely(!len)) if (unlikely(!len))
return NULL; return NULL;
BUG_ON(unlikely(len > IOP_ADMA_XOR_MAX_BYTE_COUNT)); BUG_ON(len > IOP_ADMA_XOR_MAX_BYTE_COUNT);
dev_dbg(iop_chan->device->common.dev, dev_dbg(iop_chan->device->common.dev,
"%s src_cnt: %d len: %u flags: %lx\n", "%s src_cnt: %d len: %u flags: %lx\n",
......
...@@ -671,7 +671,7 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, ...@@ -671,7 +671,7 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
return NULL; return NULL;
BUG_ON(unlikely(len > MV_XOR_MAX_BYTE_COUNT)); BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
spin_lock_bh(&mv_chan->lock); spin_lock_bh(&mv_chan->lock);
slot_cnt = mv_chan_memcpy_slot_count(len); slot_cnt = mv_chan_memcpy_slot_count(len);
...@@ -710,7 +710,7 @@ mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value, ...@@ -710,7 +710,7 @@ mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
return NULL; return NULL;
BUG_ON(unlikely(len > MV_XOR_MAX_BYTE_COUNT)); BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
spin_lock_bh(&mv_chan->lock); spin_lock_bh(&mv_chan->lock);
slot_cnt = mv_chan_memset_slot_count(len); slot_cnt = mv_chan_memset_slot_count(len);
...@@ -744,7 +744,7 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, ...@@ -744,7 +744,7 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
return NULL; return NULL;
BUG_ON(unlikely(len > MV_XOR_MAX_BYTE_COUNT)); BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
dev_dbg(mv_chan->device->common.dev, dev_dbg(mv_chan->device->common.dev,
"%s src_cnt: %d len: dest %x %u flags: %ld\n", "%s src_cnt: %d len: dest %x %u flags: %ld\n",
......
...@@ -77,10 +77,10 @@ struct pch_dma_regs { ...@@ -77,10 +77,10 @@ struct pch_dma_regs {
u32 dma_ctl0; u32 dma_ctl0;
u32 dma_ctl1; u32 dma_ctl1;
u32 dma_ctl2; u32 dma_ctl2;
u32 reserved1; u32 dma_ctl3;
u32 dma_sts0; u32 dma_sts0;
u32 dma_sts1; u32 dma_sts1;
u32 reserved2; u32 dma_sts2;
u32 reserved3; u32 reserved3;
struct pch_dma_desc_regs desc[MAX_CHAN_NR]; struct pch_dma_desc_regs desc[MAX_CHAN_NR];
}; };
...@@ -130,6 +130,7 @@ struct pch_dma { ...@@ -130,6 +130,7 @@ struct pch_dma {
#define PCH_DMA_CTL0 0x00 #define PCH_DMA_CTL0 0x00
#define PCH_DMA_CTL1 0x04 #define PCH_DMA_CTL1 0x04
#define PCH_DMA_CTL2 0x08 #define PCH_DMA_CTL2 0x08
#define PCH_DMA_CTL3 0x0C
#define PCH_DMA_STS0 0x10 #define PCH_DMA_STS0 0x10
#define PCH_DMA_STS1 0x14 #define PCH_DMA_STS1 0x14
...@@ -138,7 +139,8 @@ struct pch_dma { ...@@ -138,7 +139,8 @@ struct pch_dma {
#define dma_writel(pd, name, val) \ #define dma_writel(pd, name, val) \
writel((val), (pd)->membase + PCH_DMA_##name) writel((val), (pd)->membase + PCH_DMA_##name)
static inline struct pch_dma_desc *to_pd_desc(struct dma_async_tx_descriptor *txd) static inline
struct pch_dma_desc *to_pd_desc(struct dma_async_tx_descriptor *txd)
{ {
return container_of(txd, struct pch_dma_desc, txd); return container_of(txd, struct pch_dma_desc, txd);
} }
...@@ -163,13 +165,15 @@ static inline struct device *chan2parent(struct dma_chan *chan) ...@@ -163,13 +165,15 @@ static inline struct device *chan2parent(struct dma_chan *chan)
return chan->dev->device.parent; return chan->dev->device.parent;
} }
static inline struct pch_dma_desc *pdc_first_active(struct pch_dma_chan *pd_chan) static inline
struct pch_dma_desc *pdc_first_active(struct pch_dma_chan *pd_chan)
{ {
return list_first_entry(&pd_chan->active_list, return list_first_entry(&pd_chan->active_list,
struct pch_dma_desc, desc_node); struct pch_dma_desc, desc_node);
} }
static inline struct pch_dma_desc *pdc_first_queued(struct pch_dma_chan *pd_chan) static inline
struct pch_dma_desc *pdc_first_queued(struct pch_dma_chan *pd_chan)
{ {
return list_first_entry(&pd_chan->queue, return list_first_entry(&pd_chan->queue,
struct pch_dma_desc, desc_node); struct pch_dma_desc, desc_node);
...@@ -199,6 +203,7 @@ static void pdc_set_dir(struct dma_chan *chan) ...@@ -199,6 +203,7 @@ static void pdc_set_dir(struct dma_chan *chan)
struct pch_dma *pd = to_pd(chan->device); struct pch_dma *pd = to_pd(chan->device);
u32 val; u32 val;
if (chan->chan_id < 8) {
val = dma_readl(pd, CTL0); val = dma_readl(pd, CTL0);
if (pd_chan->dir == DMA_TO_DEVICE) if (pd_chan->dir == DMA_TO_DEVICE)
...@@ -209,6 +214,19 @@ static void pdc_set_dir(struct dma_chan *chan) ...@@ -209,6 +214,19 @@ static void pdc_set_dir(struct dma_chan *chan)
DMA_CTL0_DIR_SHIFT_BITS)); DMA_CTL0_DIR_SHIFT_BITS));
dma_writel(pd, CTL0, val); dma_writel(pd, CTL0, val);
} else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
val = dma_readl(pd, CTL3);
if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS);
else
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS));
dma_writel(pd, CTL3, val);
}
dev_dbg(chan2dev(chan), "pdc_set_dir: chan %d -> %x\n", dev_dbg(chan2dev(chan), "pdc_set_dir: chan %d -> %x\n",
chan->chan_id, val); chan->chan_id, val);
...@@ -219,6 +237,7 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode) ...@@ -219,6 +237,7 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode)
struct pch_dma *pd = to_pd(chan->device); struct pch_dma *pd = to_pd(chan->device);
u32 val; u32 val;
if (chan->chan_id < 8) {
val = dma_readl(pd, CTL0); val = dma_readl(pd, CTL0);
val &= ~(DMA_CTL0_MODE_MASK_BITS << val &= ~(DMA_CTL0_MODE_MASK_BITS <<
...@@ -226,6 +245,18 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode) ...@@ -226,6 +245,18 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode)
val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id); val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id);
dma_writel(pd, CTL0, val); dma_writel(pd, CTL0, val);
} else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
val = dma_readl(pd, CTL3);
val &= ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * ch));
val |= mode << (DMA_CTL0_BITS_PER_CH * ch);
dma_writel(pd, CTL3, val);
}
dev_dbg(chan2dev(chan), "pdc_set_mode: chan %d -> %x\n", dev_dbg(chan2dev(chan), "pdc_set_mode: chan %d -> %x\n",
chan->chan_id, val); chan->chan_id, val);
...@@ -251,9 +282,6 @@ static bool pdc_is_idle(struct pch_dma_chan *pd_chan) ...@@ -251,9 +282,6 @@ static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
static void pdc_dostart(struct pch_dma_chan *pd_chan, struct pch_dma_desc* desc) static void pdc_dostart(struct pch_dma_chan *pd_chan, struct pch_dma_desc* desc)
{ {
struct pch_dma *pd = to_pd(pd_chan->chan.device);
u32 val;
if (!pdc_is_idle(pd_chan)) { if (!pdc_is_idle(pd_chan)) {
dev_err(chan2dev(&pd_chan->chan), dev_err(chan2dev(&pd_chan->chan),
"BUG: Attempt to start non-idle channel\n"); "BUG: Attempt to start non-idle channel\n");
...@@ -279,10 +307,6 @@ static void pdc_dostart(struct pch_dma_chan *pd_chan, struct pch_dma_desc* desc) ...@@ -279,10 +307,6 @@ static void pdc_dostart(struct pch_dma_chan *pd_chan, struct pch_dma_desc* desc)
channel_writel(pd_chan, NEXT, desc->txd.phys); channel_writel(pd_chan, NEXT, desc->txd.phys);
pdc_set_mode(&pd_chan->chan, DMA_CTL0_SG); 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);
dma_writel(pd, CTL2, val);
} }
static void pdc_chain_complete(struct pch_dma_chan *pd_chan, static void pdc_chain_complete(struct pch_dma_chan *pd_chan,
...@@ -403,7 +427,7 @@ static struct pch_dma_desc *pdc_desc_get(struct pch_dma_chan *pd_chan) ...@@ -403,7 +427,7 @@ static struct pch_dma_desc *pdc_desc_get(struct pch_dma_chan *pd_chan)
{ {
struct pch_dma_desc *desc, *_d; struct pch_dma_desc *desc, *_d;
struct pch_dma_desc *ret = NULL; struct pch_dma_desc *ret = NULL;
int i; int i = 0;
spin_lock(&pd_chan->lock); spin_lock(&pd_chan->lock);
list_for_each_entry_safe(desc, _d, &pd_chan->free_list, desc_node) { list_for_each_entry_safe(desc, _d, &pd_chan->free_list, desc_node) {
...@@ -478,7 +502,6 @@ static int pd_alloc_chan_resources(struct dma_chan *chan) ...@@ -478,7 +502,6 @@ static int pd_alloc_chan_resources(struct dma_chan *chan)
spin_unlock_bh(&pd_chan->lock); spin_unlock_bh(&pd_chan->lock);
pdc_enable_irq(chan, 1); pdc_enable_irq(chan, 1);
pdc_set_dir(chan);
return pd_chan->descs_allocated; return pd_chan->descs_allocated;
} }
...@@ -561,6 +584,9 @@ static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan, ...@@ -561,6 +584,9 @@ static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan,
else else
return NULL; return NULL;
pd_chan->dir = direction;
pdc_set_dir(chan);
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
desc = pdc_desc_get(pd_chan); desc = pdc_desc_get(pd_chan);
...@@ -703,6 +729,7 @@ static void pch_dma_save_regs(struct pch_dma *pd) ...@@ -703,6 +729,7 @@ static void pch_dma_save_regs(struct pch_dma *pd)
pd->regs.dma_ctl0 = dma_readl(pd, CTL0); pd->regs.dma_ctl0 = dma_readl(pd, CTL0);
pd->regs.dma_ctl1 = dma_readl(pd, CTL1); pd->regs.dma_ctl1 = dma_readl(pd, CTL1);
pd->regs.dma_ctl2 = dma_readl(pd, CTL2); pd->regs.dma_ctl2 = dma_readl(pd, CTL2);
pd->regs.dma_ctl3 = dma_readl(pd, CTL3);
list_for_each_entry_safe(chan, _c, &pd->dma.channels, device_node) { list_for_each_entry_safe(chan, _c, &pd->dma.channels, device_node) {
pd_chan = to_pd_chan(chan); pd_chan = to_pd_chan(chan);
...@@ -725,6 +752,7 @@ static void pch_dma_restore_regs(struct pch_dma *pd) ...@@ -725,6 +752,7 @@ static void pch_dma_restore_regs(struct pch_dma *pd)
dma_writel(pd, CTL0, pd->regs.dma_ctl0); dma_writel(pd, CTL0, pd->regs.dma_ctl0);
dma_writel(pd, CTL1, pd->regs.dma_ctl1); dma_writel(pd, CTL1, pd->regs.dma_ctl1);
dma_writel(pd, CTL2, pd->regs.dma_ctl2); dma_writel(pd, CTL2, pd->regs.dma_ctl2);
dma_writel(pd, CTL3, pd->regs.dma_ctl3);
list_for_each_entry_safe(chan, _c, &pd->dma.channels, device_node) { list_for_each_entry_safe(chan, _c, &pd->dma.channels, device_node) {
pd_chan = to_pd_chan(chan); pd_chan = to_pd_chan(chan);
...@@ -850,8 +878,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, ...@@ -850,8 +878,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
pd_chan->membase = &regs->desc[i]; pd_chan->membase = &regs->desc[i];
pd_chan->dir = (i % 2) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
spin_lock_init(&pd_chan->lock); spin_lock_init(&pd_chan->lock);
INIT_LIST_HEAD(&pd_chan->active_list); INIT_LIST_HEAD(&pd_chan->active_list);
...@@ -929,13 +955,23 @@ static void __devexit pch_dma_remove(struct pci_dev *pdev) ...@@ -929,13 +955,23 @@ static void __devexit pch_dma_remove(struct pci_dev *pdev)
#define PCI_DEVICE_ID_ML7213_DMA1_8CH 0x8026 #define PCI_DEVICE_ID_ML7213_DMA1_8CH 0x8026
#define PCI_DEVICE_ID_ML7213_DMA2_8CH 0x802B #define PCI_DEVICE_ID_ML7213_DMA2_8CH 0x802B
#define PCI_DEVICE_ID_ML7213_DMA3_4CH 0x8034 #define PCI_DEVICE_ID_ML7213_DMA3_4CH 0x8034
#define PCI_DEVICE_ID_ML7213_DMA4_12CH 0x8032
#define PCI_DEVICE_ID_ML7223_DMA1_4CH 0x800B
#define PCI_DEVICE_ID_ML7223_DMA2_4CH 0x800E
#define PCI_DEVICE_ID_ML7223_DMA3_4CH 0x8017
#define PCI_DEVICE_ID_ML7223_DMA4_4CH 0x803B
static const struct pci_device_id pch_dma_id_table[] = { DEFINE_PCI_DEVICE_TABLE(pch_dma_id_table) = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_8CH), 8 }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_8CH), 8 },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_4CH), 4 }, { 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_DMA1_8CH), 8}, /* UART Video */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA2_8CH), 8}, /* PCMIF SPI */ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA2_8CH), 8}, /* PCMIF SPI */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA3_4CH), 4}, /* FPGA */ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA3_4CH), 4}, /* FPGA */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_DMA4_12CH), 12}, /* I2S */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA1_4CH), 4}, /* UART */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA2_4CH), 4}, /* Video SPI */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA3_4CH), 4}, /* Security */
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA4_4CH), 4}, /* FPGA */
{ 0, }, { 0, },
}; };
......
...@@ -2313,7 +2313,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy( ...@@ -2313,7 +2313,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy(
if (unlikely(!len)) if (unlikely(!len))
return NULL; return NULL;
BUG_ON(unlikely(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT)); BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT);
spin_lock_bh(&ppc440spe_chan->lock); spin_lock_bh(&ppc440spe_chan->lock);
...@@ -2354,7 +2354,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memset( ...@@ -2354,7 +2354,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memset(
if (unlikely(!len)) if (unlikely(!len))
return NULL; return NULL;
BUG_ON(unlikely(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT)); BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT);
spin_lock_bh(&ppc440spe_chan->lock); spin_lock_bh(&ppc440spe_chan->lock);
...@@ -2397,7 +2397,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor( ...@@ -2397,7 +2397,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor(
dma_dest, dma_src, src_cnt)); dma_dest, dma_src, src_cnt));
if (unlikely(!len)) if (unlikely(!len))
return NULL; return NULL;
BUG_ON(unlikely(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT)); BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
dev_dbg(ppc440spe_chan->device->common.dev, dev_dbg(ppc440spe_chan->device->common.dev,
"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n", "ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
...@@ -2887,7 +2887,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq( ...@@ -2887,7 +2887,7 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq(
ADMA_LL_DBG(prep_dma_pq_dbg(ppc440spe_chan->device->id, ADMA_LL_DBG(prep_dma_pq_dbg(ppc440spe_chan->device->id,
dst, src, src_cnt)); dst, src, src_cnt));
BUG_ON(!len); BUG_ON(!len);
BUG_ON(unlikely(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT)); BUG_ON(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT);
BUG_ON(!src_cnt); BUG_ON(!src_cnt);
if (src_cnt == 1 && dst[1] == src[0]) { if (src_cnt == 1 && dst[1] == src[0]) {
......
...@@ -1829,7 +1829,7 @@ d40_get_dev_addr(struct d40_chan *chan, enum dma_data_direction direction) ...@@ -1829,7 +1829,7 @@ d40_get_dev_addr(struct d40_chan *chan, enum dma_data_direction direction)
{ {
struct stedma40_platform_data *plat = chan->base->plat_data; struct stedma40_platform_data *plat = chan->base->plat_data;
struct stedma40_chan_cfg *cfg = &chan->dma_cfg; struct stedma40_chan_cfg *cfg = &chan->dma_cfg;
dma_addr_t addr; dma_addr_t addr = 0;
if (chan->runtime_addr) if (chan->runtime_addr)
return chan->runtime_addr; return chan->runtime_addr;
...@@ -2962,4 +2962,4 @@ static int __init stedma40_init(void) ...@@ -2962,4 +2962,4 @@ static int __init stedma40_init(void)
{ {
return platform_driver_probe(&d40_driver, d40_probe); return platform_driver_probe(&d40_driver, d40_probe);
} }
arch_initcall(stedma40_init); subsys_initcall(stedma40_init);
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* AVR32 systems.) * AVR32 systems.)
* *
* Copyright (C) 2007 Atmel Corporation * Copyright (C) 2007 Atmel Corporation
* Copyright (C) 2010-2011 ST Microelectronics
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment