Commit 12ff47e7 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma

* 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma: (37 commits)
  Improve slave/cyclic DMA engine documentation
  dmaengine: pl08x: handle the rest of enums in pl08x_width
  DMA: PL08x: cleanup selection of burst size
  DMA: PL08x: avoid recalculating cctl at each prepare
  DMA: PL08x: cleanup selection of buswidth
  DMA: PL08x: constify plchan->cd and plat->slave_channels
  DMA: PL08x: separately store source/destination cctl
  DMA: PL08x: separately store source/destination slave address
  DMA: PL08x: clean up LLI debugging
  DMA: PL08x: select LLI bus only once per LLI setup
  DMA: PL08x: remove unused constants
  ARM: mxs-dma: reset after disable channel
  dma: intel_mid_dma: remove redundant pci_set_drvdata calls
  dma: mxs-dma: fix unterminated platform_device_id table
  dmaengine: pl330: make platform data optional
  dmaengine: imx-sdma: return proper error if kzalloc fails
  pch_dma: Fix CTL register access issue
  dmaengine: mxs-dma: skip request_irq for NO_IRQ
  dmaengine/coh901318: fix slave submission semantics
  dmaengine/ste_dma40: allow memory buswidth/burst to be configured
  ...

Fix trivial whitespace conflict in drivers/dma/mv_xor.c
parents 73bcbac1 1ae105aa
...@@ -10,87 +10,181 @@ NOTE: For DMA Engine usage in async_tx please see: ...@@ -10,87 +10,181 @@ NOTE: For DMA Engine usage in async_tx please see:
Below is a guide to device driver writers on how to use the Slave-DMA API of the 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. DMA Engine. This is applicable only for slave DMA usage only.
The slave DMA usage consists of following steps The slave DMA usage consists of following steps:
1. Allocate a DMA slave channel 1. Allocate a DMA slave channel
2. Set slave and controller specific parameters 2. Set slave and controller specific parameters
3. Get a descriptor for transaction 3. Get a descriptor for transaction
4. Submit the transaction and wait for callback notification 4. Submit the transaction
5. Issue pending requests and wait for callback notification
1. Allocate a DMA slave channel 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 Channel allocation is slightly different in the slave DMA context,
in some cases a specific channel is desired. To request a channel client drivers typically need a channel from a particular DMA
dma_request_channel() API is used. 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, Interface:
dma_filter_fn filter_fn, struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
void *filter_param); dma_filter_fn filter_fn,
where dma_filter_fn is defined as: void *filter_param);
typedef bool (*dma_filter_fn)(struct dma_chan *chan, 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, The 'filter_fn' parameter is optional, but highly recommended for
when the mask parameter is insufficient for specifying the necessary channel, slave and cyclic channels as they typically need to obtain a specific
the filter_fn routine can be used to disposition the available channels in the DMA channel.
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 When the optional 'filter_fn' parameter is NULL, dma_request_channel()
that channel to be the return value from dma_request_channel. A channel simply returns the first channel that satisfies the capability mask.
allocated via this interface is exclusive to the caller, until
dma_release_channel() is called. Otherwise, the 'filter_fn' routine will be called once for each free
channel which has a capability in 'mask'. 'filter_fn' is expected to
return 'true' when the desired DMA channel is found.
A channel allocated via this interface is exclusive to the caller,
until dma_release_channel() is called.
2. Set slave and controller specific parameters 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. Next step is always to pass some specific information to the DMA
It allows the clients to specify DMA direction, DMA addresses, bus widths, DMA driver. Most of the generic information which a slave DMA can use
burst lengths etc. If some DMA controllers have more parameters to be sent then is in struct dma_slave_config. This allows the clients to specify
they should try to embed struct dma_slave_config in their controller specific DMA direction, DMA addresses, bus widths, DMA burst lengths etc
structure. That gives flexibility to client to pass more parameters, if for the peripheral.
required.
If some DMA controllers have more parameters to be sent then they
Interface: should try to embed struct dma_slave_config in their controller
int dmaengine_slave_config(struct dma_chan *chan, specific structure. That gives flexibility to client to pass more
struct dma_slave_config *config) parameters, if required.
Interface:
int dmaengine_slave_config(struct dma_chan *chan,
struct dma_slave_config *config)
Please see the dma_slave_config structure definition in dmaengine.h
for a detailed explaination of the struct members. Please note
that the 'direction' member will be going away as it duplicates the
direction given in the prepare call.
3. Get a descriptor for transaction 3. Get a descriptor for transaction
For slave usage the various modes of slave transfers supported by the
DMA-engine are: For slave usage the various modes of slave transfers supported by the
slave_sg - DMA a list of scatter gather buffers from/to a peripheral DMA-engine are:
dma_cyclic - Perform a cyclic DMA operation from/to a peripheral till the
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. operation is explicitly stopped.
The non NULL return of this transfer API represents a "descriptor" for the given
transaction. A 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)( Interface:
struct dma_chan *chan, struct dma_async_tx_descriptor *(*chan->device->device_prep_slave_sg)(
struct scatterlist *dst_sg, unsigned int dst_nents, struct dma_chan *chan, struct scatterlist *sgl,
struct scatterlist *src_sg, unsigned int src_nents, unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags); unsigned long flags);
struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_data_direction direction); size_t period_len, enum dma_data_direction direction);
4. Submit the transaction and wait for callback notification The peripheral driver is expected to have mapped the scatterlist for
To schedule the transaction to be scheduled by dma device, the "descriptor" the DMA operation prior to calling device_prep_slave_sg, and must
returned in above (3) needs to be submitted. keep the scatterlist mapped until the DMA operation has completed.
To tell the dma driver that a transaction is ready to be serviced, the The scatterlist must be mapped using the DMA struct device. So,
descriptor->submit() callback needs to be invoked. This chains the descriptor to normal setup should look like this:
the pending queue.
The transactions in the pending queue can be activated by calling the nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
issue_pending API. If channel is idle then the first transaction in queue is if (nr_sg == 0)
started and subsequent ones queued up. /* error */
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 desc = chan->device->device_prep_slave_sg(chan, sgl, nr_sg,
routine for notification, if set. direction, flags);
Interface:
void dma_async_issue_pending(struct dma_chan *chan); Once a descriptor has been obtained, the callback information can be
added and the descriptor must then be submitted. Some DMA engine
============================================================================== drivers may hold a spinlock between a successful preparation and
submission so it is important that these two operations are closely
Additional usage notes for dma driver writers paired.
1/ Although DMA engine specifies that completion callback routines cannot submit
any new operations, but typically for slave DMA subsequent transaction may not Note:
be available for submit prior to callback routine being called. This requirement Although the async_tx API specifies that completion callback
is not a requirement for DMA-slave devices. But they should take care to drop routines cannot submit any new operations, this is not the
the spin-lock they might be holding before calling the callback routine case for slave/cyclic DMA.
For slave DMA, the subsequent transaction may not be available
for submission prior to callback function being invoked, so
slave DMA callbacks are permitted to prepare and submit a new
transaction.
For cyclic DMA, a callback function may wish to terminate the
DMA via dmaengine_terminate_all().
Therefore, it is important that DMA engine drivers drop any
locks before calling the callback function which may cause a
deadlock.
Note that callbacks will always be invoked from the DMA
engines tasklet, never from interrupt context.
4. Submit the transaction
Once the descriptor has been prepared and the callback information
added, it must be placed on the DMA engine drivers pending queue.
Interface:
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
This returns a cookie can be used to check the progress of DMA engine
activity via other DMA engine calls not covered in this document.
dmaengine_submit() will not start the DMA operation, it merely adds
it to the pending queue. For this, see step 5, dma_async_issue_pending.
5. Issue pending DMA requests and wait for callback notification
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 each DMA operation, the next in queue is started and
a tasklet triggered. The tasklet will then call the client driver
completion callback routine for notification, if set.
Interface:
void dma_async_issue_pending(struct dma_chan *chan);
Further APIs:
1. int dmaengine_terminate_all(struct dma_chan *chan)
This causes all activity for the DMA channel to be stopped, and may
discard data in the DMA FIFO which hasn't been fully transferred.
No callback functions will be called for any incomplete transfers.
2. int dmaengine_pause(struct dma_chan *chan)
This pauses activity on the DMA channel without data loss.
3. int dmaengine_resume(struct dma_chan *chan)
Resume a previously paused DMA channel. It is invalid to resume a
channel which is not currently paused.
4. enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
This can be used to check the status of the channel. Please see
the documentation in include/linux/dmaengine.h for a more complete
description of this API.
This can be used in conjunction with dma_async_is_complete() and
the cookie returned from 'descriptor->submit()' to check for
completion of a specific DMA transaction.
Note:
Not all DMA engine drivers can return reliable information for
a running DMA channel. It is recommended that DMA engine users
pause or stop (via dmaengine_terminate_all) the channel before
using this API.
...@@ -9,6 +9,5 @@ TODO for slave dma ...@@ -9,6 +9,5 @@ TODO for slave dma
- mxs-dma.c - mxs-dma.c
- dw_dmac - dw_dmac
- intel_mid_dma - intel_mid_dma
- ste_dma40
4. Check other subsystems for dma drivers and merge/move to dmaengine 4. Check other subsystems for dma drivers and merge/move to dmaengine
5. Remove dma_slave_config's dma direction. 5. Remove dma_slave_config's dma direction.
...@@ -156,14 +156,10 @@ struct pl08x_driver_data { ...@@ -156,14 +156,10 @@ struct pl08x_driver_data {
#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */ #define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */
#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT) #define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT)
/* Minimum period between work queue runs */
#define PL08X_WQ_PERIODMIN 20
/* Size (bytes) of each LLI buffer allocated for one transfer */ /* Size (bytes) of each LLI buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000 # define PL08X_LLI_TSFR_SIZE 0x2000
/* Maximum times we call dma_pool_alloc on this pool without freeing */ /* Maximum times we call dma_pool_alloc on this pool without freeing */
#define PL08X_MAX_ALLOCS 0x40
#define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli)) #define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli))
#define PL08X_ALIGN 8 #define PL08X_ALIGN 8
...@@ -495,10 +491,10 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth, ...@@ -495,10 +491,10 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
struct pl08x_lli_build_data { struct pl08x_lli_build_data {
struct pl08x_txd *txd; struct pl08x_txd *txd;
struct pl08x_driver_data *pl08x;
struct pl08x_bus_data srcbus; struct pl08x_bus_data srcbus;
struct pl08x_bus_data dstbus; struct pl08x_bus_data dstbus;
size_t remainder; size_t remainder;
u32 lli_bus;
}; };
/* /*
...@@ -551,8 +547,7 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd, ...@@ -551,8 +547,7 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
llis_va[num_llis].src = bd->srcbus.addr; llis_va[num_llis].src = bd->srcbus.addr;
llis_va[num_llis].dst = bd->dstbus.addr; llis_va[num_llis].dst = bd->dstbus.addr;
llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli); llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
if (bd->pl08x->lli_buses & PL08X_AHB2) llis_va[num_llis].lli |= bd->lli_bus;
llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
if (cctl & PL080_CONTROL_SRC_INCR) if (cctl & PL080_CONTROL_SRC_INCR)
bd->srcbus.addr += len; bd->srcbus.addr += len;
...@@ -605,9 +600,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, ...@@ -605,9 +600,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
cctl = txd->cctl; cctl = txd->cctl;
bd.txd = txd; bd.txd = txd;
bd.pl08x = pl08x;
bd.srcbus.addr = txd->src_addr; bd.srcbus.addr = txd->src_addr;
bd.dstbus.addr = txd->dst_addr; bd.dstbus.addr = txd->dst_addr;
bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
/* Find maximum width of the source bus */ /* Find maximum width of the source bus */
bd.srcbus.maxwidth = bd.srcbus.maxwidth =
...@@ -622,25 +617,15 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, ...@@ -622,25 +617,15 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
/* Set up the bus widths to the maximum */ /* Set up the bus widths to the maximum */
bd.srcbus.buswidth = bd.srcbus.maxwidth; bd.srcbus.buswidth = bd.srcbus.maxwidth;
bd.dstbus.buswidth = bd.dstbus.maxwidth; bd.dstbus.buswidth = bd.dstbus.maxwidth;
dev_vdbg(&pl08x->adev->dev,
"%s source bus is %d bytes wide, dest bus is %d bytes wide\n",
__func__, bd.srcbus.buswidth, bd.dstbus.buswidth);
/* /*
* Bytes transferred == tsize * MIN(buswidths), not max(buswidths) * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
*/ */
max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) * max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
PL080_CONTROL_TRANSFER_SIZE_MASK; PL080_CONTROL_TRANSFER_SIZE_MASK;
dev_vdbg(&pl08x->adev->dev,
"%s max bytes per lli = %zu\n",
__func__, max_bytes_per_lli);
/* We need to count this down to zero */ /* We need to count this down to zero */
bd.remainder = txd->len; bd.remainder = txd->len;
dev_vdbg(&pl08x->adev->dev,
"%s remainder = %zu\n",
__func__, bd.remainder);
/* /*
* Choose bus to align to * Choose bus to align to
...@@ -649,6 +634,16 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, ...@@ -649,6 +634,16 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
*/ */
pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n",
bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
bd.srcbus.buswidth,
bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
bd.dstbus.buswidth,
bd.remainder, max_bytes_per_lli);
dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
mbus == &bd.srcbus ? "src" : "dst",
sbus == &bd.srcbus ? "src" : "dst");
if (txd->len < mbus->buswidth) { if (txd->len < mbus->buswidth) {
/* Less than a bus width available - send as single bytes */ /* Less than a bus width available - send as single bytes */
while (bd.remainder) { while (bd.remainder) {
...@@ -840,15 +835,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, ...@@ -840,15 +835,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
{ {
int i; int i;
dev_vdbg(&pl08x->adev->dev,
"%-3s %-9s %-10s %-10s %-10s %s\n",
"lli", "", "csrc", "cdst", "clli", "cctl");
for (i = 0; i < num_llis; i++) { for (i = 0; i < num_llis; i++) {
dev_vdbg(&pl08x->adev->dev, dev_vdbg(&pl08x->adev->dev,
"lli %d @%p: csrc=0x%08x, cdst=0x%08x, cctl=0x%08x, clli=0x%08x\n", "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n",
i, i, &llis_va[i], llis_va[i].src,
&llis_va[i], llis_va[i].dst, llis_va[i].lli, llis_va[i].cctl
llis_va[i].src,
llis_va[i].dst,
llis_va[i].cctl,
llis_va[i].lli
); );
} }
} }
...@@ -1054,64 +1048,105 @@ pl08x_dma_tx_status(struct dma_chan *chan, ...@@ -1054,64 +1048,105 @@ pl08x_dma_tx_status(struct dma_chan *chan,
/* PrimeCell DMA extension */ /* PrimeCell DMA extension */
struct burst_table { struct burst_table {
int burstwords; u32 burstwords;
u32 reg; u32 reg;
}; };
static const struct burst_table burst_sizes[] = { static const struct burst_table burst_sizes[] = {
{ {
.burstwords = 256, .burstwords = 256,
.reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_256,
(PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 128, .burstwords = 128,
.reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_128,
(PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 64, .burstwords = 64,
.reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_64,
(PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 32, .burstwords = 32,
.reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_32,
(PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 16, .burstwords = 16,
.reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_16,
(PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 8, .burstwords = 8,
.reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_8,
(PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 4, .burstwords = 4,
.reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_4,
(PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
{ {
.burstwords = 1, .burstwords = 0,
.reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) | .reg = PL080_BSIZE_1,
(PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT),
}, },
}; };
/*
* Given the source and destination available bus masks, select which
* will be routed to each port. We try to have source and destination
* on separate ports, but always respect the allowable settings.
*/
static u32 pl08x_select_bus(u8 src, u8 dst)
{
u32 cctl = 0;
if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
cctl |= PL080_CONTROL_DST_AHB2;
if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
cctl |= PL080_CONTROL_SRC_AHB2;
return cctl;
}
static u32 pl08x_cctl(u32 cctl)
{
cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
PL080_CONTROL_PROT_MASK);
/* Access the cell in privileged mode, non-bufferable, non-cacheable */
return cctl | PL080_CONTROL_PROT_SYS;
}
static u32 pl08x_width(enum dma_slave_buswidth width)
{
switch (width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
return PL080_WIDTH_8BIT;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
return PL080_WIDTH_16BIT;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
return PL080_WIDTH_32BIT;
default:
return ~0;
}
}
static u32 pl08x_burst(u32 maxburst)
{
int i;
for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
if (burst_sizes[i].burstwords <= maxburst)
break;
return burst_sizes[i].reg;
}
static int dma_set_runtime_config(struct dma_chan *chan, static int dma_set_runtime_config(struct dma_chan *chan,
struct dma_slave_config *config) struct dma_slave_config *config)
{ {
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_channel_data *cd = plchan->cd;
enum dma_slave_buswidth addr_width; enum dma_slave_buswidth addr_width;
dma_addr_t addr; u32 width, burst, maxburst;
u32 maxburst;
u32 cctl = 0; u32 cctl = 0;
int i;
if (!plchan->slave) if (!plchan->slave)
return -EINVAL; return -EINVAL;
...@@ -1119,11 +1154,9 @@ static int dma_set_runtime_config(struct dma_chan *chan, ...@@ -1119,11 +1154,9 @@ static int dma_set_runtime_config(struct dma_chan *chan,
/* Transfer direction */ /* Transfer direction */
plchan->runtime_direction = config->direction; plchan->runtime_direction = config->direction;
if (config->direction == DMA_TO_DEVICE) { if (config->direction == DMA_TO_DEVICE) {
addr = config->dst_addr;
addr_width = config->dst_addr_width; addr_width = config->dst_addr_width;
maxburst = config->dst_maxburst; maxburst = config->dst_maxburst;
} else if (config->direction == DMA_FROM_DEVICE) { } else if (config->direction == DMA_FROM_DEVICE) {
addr = config->src_addr;
addr_width = config->src_addr_width; addr_width = config->src_addr_width;
maxburst = config->src_maxburst; maxburst = config->src_maxburst;
} else { } else {
...@@ -1132,46 +1165,40 @@ static int dma_set_runtime_config(struct dma_chan *chan, ...@@ -1132,46 +1165,40 @@ static int dma_set_runtime_config(struct dma_chan *chan,
return -EINVAL; return -EINVAL;
} }
switch (addr_width) { width = pl08x_width(addr_width);
case DMA_SLAVE_BUSWIDTH_1_BYTE: if (width == ~0) {
cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) |
(PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT);
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) |
(PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT);
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) |
(PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT);
break;
default:
dev_err(&pl08x->adev->dev, dev_err(&pl08x->adev->dev,
"bad runtime_config: alien address width\n"); "bad runtime_config: alien address width\n");
return -EINVAL; return -EINVAL;
} }
cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
/* /*
* Now decide on a maxburst:
* If this channel will only request single transfers, set this * If this channel will only request single transfers, set this
* down to ONE element. Also select one element if no maxburst * down to ONE element. Also select one element if no maxburst
* is specified. * is specified.
*/ */
if (plchan->cd->single || maxburst == 0) { if (plchan->cd->single)
cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) | maxburst = 1;
(PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
burst = pl08x_burst(maxburst);
cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
if (plchan->runtime_direction == DMA_FROM_DEVICE) {
plchan->src_addr = config->src_addr;
plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
pl08x_select_bus(plchan->cd->periph_buses,
pl08x->mem_buses);
} else { } else {
for (i = 0; i < ARRAY_SIZE(burst_sizes); i++) plchan->dst_addr = config->dst_addr;
if (burst_sizes[i].burstwords <= maxburst) plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
break; pl08x_select_bus(pl08x->mem_buses,
cctl |= burst_sizes[i].reg; plchan->cd->periph_buses);
} }
plchan->runtime_addr = addr;
/* Modify the default channel data to fit PrimeCell request */
cd->cctl = cctl;
dev_dbg(&pl08x->adev->dev, dev_dbg(&pl08x->adev->dev,
"configured channel %s (%s) for %s, data width %d, " "configured channel %s (%s) for %s, data width %d, "
"maxburst %d words, LE, CCTL=0x%08x\n", "maxburst %d words, LE, CCTL=0x%08x\n",
...@@ -1270,23 +1297,6 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, ...@@ -1270,23 +1297,6 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
return 0; return 0;
} }
/*
* Given the source and destination available bus masks, select which
* will be routed to each port. We try to have source and destination
* on separate ports, but always respect the allowable settings.
*/
static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
{
u32 cctl = 0;
if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
cctl |= PL080_CONTROL_DST_AHB2;
if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
cctl |= PL080_CONTROL_SRC_AHB2;
return cctl;
}
static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan, static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
unsigned long flags) unsigned long flags)
{ {
...@@ -1338,8 +1348,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( ...@@ -1338,8 +1348,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR; txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
if (pl08x->vd->dualmaster) if (pl08x->vd->dualmaster)
txd->cctl |= pl08x_select_bus(pl08x, txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
pl08x->mem_buses, pl08x->mem_buses); pl08x->mem_buses);
ret = pl08x_prep_channel_resources(plchan, txd); ret = pl08x_prep_channel_resources(plchan, txd);
if (ret) if (ret)
...@@ -1356,7 +1366,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( ...@@ -1356,7 +1366,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_txd *txd; struct pl08x_txd *txd;
u8 src_buses, dst_buses;
int ret; int ret;
/* /*
...@@ -1390,42 +1399,22 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( ...@@ -1390,42 +1399,22 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
txd->direction = direction; txd->direction = direction;
txd->len = sgl->length; txd->len = sgl->length;
txd->cctl = plchan->cd->cctl &
~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
PL080_CONTROL_PROT_MASK);
/* Access the cell in privileged mode, non-bufferable, non-cacheable */
txd->cctl |= PL080_CONTROL_PROT_SYS;
if (direction == DMA_TO_DEVICE) { if (direction == DMA_TO_DEVICE) {
txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT; txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
txd->cctl |= PL080_CONTROL_SRC_INCR; txd->cctl = plchan->dst_cctl;
txd->src_addr = sgl->dma_address; txd->src_addr = sgl->dma_address;
if (plchan->runtime_addr) txd->dst_addr = plchan->dst_addr;
txd->dst_addr = plchan->runtime_addr;
else
txd->dst_addr = plchan->cd->addr;
src_buses = pl08x->mem_buses;
dst_buses = plchan->cd->periph_buses;
} else if (direction == DMA_FROM_DEVICE) { } else if (direction == DMA_FROM_DEVICE) {
txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
txd->cctl |= PL080_CONTROL_DST_INCR; txd->cctl = plchan->src_cctl;
if (plchan->runtime_addr) txd->src_addr = plchan->src_addr;
txd->src_addr = plchan->runtime_addr;
else
txd->src_addr = plchan->cd->addr;
txd->dst_addr = sgl->dma_address; txd->dst_addr = sgl->dma_address;
src_buses = plchan->cd->periph_buses;
dst_buses = pl08x->mem_buses;
} else { } else {
dev_err(&pl08x->adev->dev, dev_err(&pl08x->adev->dev,
"%s direction unsupported\n", __func__); "%s direction unsupported\n", __func__);
return NULL; return NULL;
} }
txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
ret = pl08x_prep_channel_resources(plchan, txd); ret = pl08x_prep_channel_resources(plchan, txd);
if (ret) if (ret)
return NULL; return NULL;
...@@ -1676,6 +1665,20 @@ static irqreturn_t pl08x_irq(int irq, void *dev) ...@@ -1676,6 +1665,20 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
return mask ? IRQ_HANDLED : IRQ_NONE; return mask ? IRQ_HANDLED : IRQ_NONE;
} }
static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
{
u32 cctl = pl08x_cctl(chan->cd->cctl);
chan->slave = true;
chan->name = chan->cd->bus_id;
chan->src_addr = chan->cd->addr;
chan->dst_addr = chan->cd->addr;
chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
}
/* /*
* Initialise the DMAC memcpy/slave channels. * Initialise the DMAC memcpy/slave channels.
* Make a local wrapper to hold required data * Make a local wrapper to hold required data
...@@ -1707,9 +1710,8 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, ...@@ -1707,9 +1710,8 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
chan->state = PL08X_CHAN_IDLE; chan->state = PL08X_CHAN_IDLE;
if (slave) { if (slave) {
chan->slave = true;
chan->name = pl08x->pd->slave_channels[i].bus_id;
chan->cd = &pl08x->pd->slave_channels[i]; chan->cd = &pl08x->pd->slave_channels[i];
pl08x_dma_slave_init(chan);
} else { } else {
chan->cd = &pl08x->pd->memcpy_channel; chan->cd = &pl08x->pd->memcpy_channel;
chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i); chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
......
...@@ -1216,7 +1216,7 @@ static int __init at_dma_probe(struct platform_device *pdev) ...@@ -1216,7 +1216,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
atdma->dma_common.cap_mask = pdata->cap_mask; atdma->dma_common.cap_mask = pdata->cap_mask;
atdma->all_chan_mask = (1 << pdata->nr_channels) - 1; atdma->all_chan_mask = (1 << pdata->nr_channels) - 1;
size = io->end - io->start + 1; size = resource_size(io);
if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { if (!request_mem_region(io->start, size, pdev->dev.driver->name)) {
err = -EBUSY; err = -EBUSY;
goto err_kfree; goto err_kfree;
...@@ -1362,7 +1362,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) ...@@ -1362,7 +1362,7 @@ static int __exit at_dma_remove(struct platform_device *pdev)
atdma->regs = NULL; atdma->regs = NULL;
io = platform_get_resource(pdev, IORESOURCE_MEM, 0); io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(io->start, io->end - io->start + 1); release_mem_region(io->start, resource_size(io));
kfree(atdma); kfree(atdma);
......
...@@ -41,6 +41,8 @@ struct coh901318_desc { ...@@ -41,6 +41,8 @@ struct coh901318_desc {
struct coh901318_lli *lli; struct coh901318_lli *lli;
enum dma_data_direction dir; enum dma_data_direction dir;
unsigned long flags; unsigned long flags;
u32 head_config;
u32 head_ctrl;
}; };
struct coh901318_base { struct coh901318_base {
...@@ -661,6 +663,9 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc) ...@@ -661,6 +663,9 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc)
coh901318_desc_submit(cohc, cohd); coh901318_desc_submit(cohc, cohd);
/* Program the transaction head */
coh901318_set_conf(cohc, cohd->head_config);
coh901318_set_ctrl(cohc, cohd->head_ctrl);
coh901318_prep_linked_list(cohc, cohd->lli); coh901318_prep_linked_list(cohc, cohd->lli);
/* start dma job on this channel */ /* start dma job on this channel */
...@@ -1091,8 +1096,6 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -1091,8 +1096,6 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
} else } else
goto err_direction; goto err_direction;
coh901318_set_conf(cohc, config);
/* The dma only supports transmitting packages up to /* The dma only supports transmitting packages up to
* MAX_DMA_PACKET_SIZE. Calculate to total number of * MAX_DMA_PACKET_SIZE. Calculate to total number of
* dma elemts required to send the entire sg list * dma elemts required to send the entire sg list
...@@ -1129,16 +1132,18 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -1129,16 +1132,18 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (ret) if (ret)
goto err_lli_fill; goto err_lli_fill;
/*
* Set the default ctrl for the channel to the one from the lli,
* things may have changed due to odd buffer alignment etc.
*/
coh901318_set_ctrl(cohc, lli->control);
COH_DBG(coh901318_list_print(cohc, lli)); COH_DBG(coh901318_list_print(cohc, lli));
/* Pick a descriptor to handle this transfer */ /* Pick a descriptor to handle this transfer */
cohd = coh901318_desc_get(cohc); cohd = coh901318_desc_get(cohc);
cohd->head_config = config;
/*
* Set the default head ctrl for the channel to the one from the
* lli, things may have changed due to odd buffer alignment
* etc.
*/
cohd->head_ctrl = lli->control;
cohd->dir = direction; cohd->dir = direction;
cohd->flags = flags; cohd->flags = flags;
cohd->desc.tx_submit = coh901318_tx_submit; cohd->desc.tx_submit = coh901318_tx_submit;
......
...@@ -510,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v ...@@ -510,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
dma_chan_name(chan)); dma_chan_name(chan));
list_del_rcu(&device->global_node); list_del_rcu(&device->global_node);
} else if (err) } else if (err)
pr_err("dmaengine: failed to get %s: (%d)\n", pr_debug("dmaengine: failed to get %s: (%d)\n",
dma_chan_name(chan), err); dma_chan_name(chan), err);
else else
break; break;
if (--device->privatecnt == 0) if (--device->privatecnt == 0)
......
...@@ -902,7 +902,7 @@ static void ep93xx_dma_free_chan_resources(struct dma_chan *chan) ...@@ -902,7 +902,7 @@ static void ep93xx_dma_free_chan_resources(struct dma_chan *chan)
* *
* Returns a valid DMA descriptor or %NULL in case of failure. * Returns a valid DMA descriptor or %NULL in case of failure.
*/ */
struct dma_async_tx_descriptor * static struct dma_async_tx_descriptor *
ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
dma_addr_t src, size_t len, unsigned long flags) dma_addr_t src, size_t len, unsigned long flags)
{ {
......
...@@ -1305,8 +1305,10 @@ static int __init sdma_probe(struct platform_device *pdev) ...@@ -1305,8 +1305,10 @@ static int __init sdma_probe(struct platform_device *pdev)
goto err_request_irq; goto err_request_irq;
sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL); sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
if (!sdma->script_addrs) if (!sdma->script_addrs) {
ret = -ENOMEM;
goto err_alloc; goto err_alloc;
}
if (of_id) if (of_id)
pdev->id_entry = of_id->data; pdev->id_entry = of_id->data;
......
...@@ -1351,7 +1351,6 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state) ...@@ -1351,7 +1351,6 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state)
return -EAGAIN; return -EAGAIN;
} }
device->state = SUSPENDED; device->state = SUSPENDED;
pci_set_drvdata(pci, device);
pci_save_state(pci); pci_save_state(pci);
pci_disable_device(pci); pci_disable_device(pci);
pci_set_power_state(pci, PCI_D3hot); pci_set_power_state(pci, PCI_D3hot);
...@@ -1380,7 +1379,6 @@ int dma_resume(struct pci_dev *pci) ...@@ -1380,7 +1379,6 @@ int dma_resume(struct pci_dev *pci)
} }
device->state = RUNNING; device->state = RUNNING;
iowrite32(REG_BIT0, device->dma_base + DMA_CFG); iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
pci_set_drvdata(pci, device);
return 0; return 0;
} }
......
...@@ -1706,16 +1706,14 @@ static int __init ipu_probe(struct platform_device *pdev) ...@@ -1706,16 +1706,14 @@ static int __init ipu_probe(struct platform_device *pdev)
ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base); ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base);
/* Remap IPU common registers */ /* Remap IPU common registers */
ipu_data.reg_ipu = ioremap(mem_ipu->start, ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu));
mem_ipu->end - mem_ipu->start + 1);
if (!ipu_data.reg_ipu) { if (!ipu_data.reg_ipu) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_ioremap_ipu; goto err_ioremap_ipu;
} }
/* Remap Image Converter and Image DMA Controller registers */ /* Remap Image Converter and Image DMA Controller registers */
ipu_data.reg_ic = ioremap(mem_ic->start, ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic));
mem_ic->end - mem_ic->start + 1);
if (!ipu_data.reg_ic) { if (!ipu_data.reg_ic) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_ioremap_ic; goto err_ioremap_ic;
......
...@@ -1304,7 +1304,8 @@ static int mv_xor_shared_probe(struct platform_device *pdev) ...@@ -1304,7 +1304,8 @@ static int mv_xor_shared_probe(struct platform_device *pdev)
if (!res) if (!res)
return -ENODEV; return -ENODEV;
msp->xor_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); msp->xor_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!msp->xor_base) if (!msp->xor_base)
return -EBUSY; return -EBUSY;
......
...@@ -327,10 +327,12 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) ...@@ -327,10 +327,12 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
memset(mxs_chan->ccw, 0, PAGE_SIZE); memset(mxs_chan->ccw, 0, PAGE_SIZE);
ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler, if (mxs_chan->chan_irq != NO_IRQ) {
0, "mxs-dma", mxs_dma); ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
if (ret) 0, "mxs-dma", mxs_dma);
goto err_irq; if (ret)
goto err_irq;
}
ret = clk_enable(mxs_dma->clk); ret = clk_enable(mxs_dma->clk);
if (ret) if (ret)
...@@ -535,6 +537,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -535,6 +537,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
switch (cmd) { switch (cmd) {
case DMA_TERMINATE_ALL: case DMA_TERMINATE_ALL:
mxs_dma_disable_chan(mxs_chan); mxs_dma_disable_chan(mxs_chan);
mxs_dma_reset_chan(mxs_chan);
break; break;
case DMA_PAUSE: case DMA_PAUSE:
mxs_dma_pause_chan(mxs_chan); mxs_dma_pause_chan(mxs_chan);
...@@ -707,6 +710,8 @@ static struct platform_device_id mxs_dma_type[] = { ...@@ -707,6 +710,8 @@ static struct platform_device_id mxs_dma_type[] = {
}, { }, {
.name = "mxs-dma-apbx", .name = "mxs-dma-apbx",
.driver_data = MXS_DMA_APBX, .driver_data = MXS_DMA_APBX,
}, {
/* end of list */
} }
}; };
......
...@@ -45,7 +45,8 @@ ...@@ -45,7 +45,8 @@
#define DMA_STATUS_MASK_BITS 0x3 #define DMA_STATUS_MASK_BITS 0x3
#define DMA_STATUS_SHIFT_BITS 16 #define DMA_STATUS_SHIFT_BITS 16
#define DMA_STATUS_IRQ(x) (0x1 << (x)) #define DMA_STATUS_IRQ(x) (0x1 << (x))
#define DMA_STATUS_ERR(x) (0x1 << ((x) + 8)) #define DMA_STATUS0_ERR(x) (0x1 << ((x) + 8))
#define DMA_STATUS2_ERR(x) (0x1 << (x))
#define DMA_DESC_WIDTH_SHIFT_BITS 12 #define DMA_DESC_WIDTH_SHIFT_BITS 12
#define DMA_DESC_WIDTH_1_BYTE (0x3 << DMA_DESC_WIDTH_SHIFT_BITS) #define DMA_DESC_WIDTH_1_BYTE (0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
...@@ -61,6 +62,9 @@ ...@@ -61,6 +62,9 @@
#define MAX_CHAN_NR 8 #define MAX_CHAN_NR 8
#define DMA_MASK_CTL0_MODE 0x33333333
#define DMA_MASK_CTL2_MODE 0x00003333
static unsigned int init_nr_desc_per_channel = 64; static unsigned int init_nr_desc_per_channel = 64;
module_param(init_nr_desc_per_channel, uint, 0644); module_param(init_nr_desc_per_channel, uint, 0644);
MODULE_PARM_DESC(init_nr_desc_per_channel, MODULE_PARM_DESC(init_nr_desc_per_channel,
...@@ -133,6 +137,7 @@ struct pch_dma { ...@@ -133,6 +137,7 @@ struct pch_dma {
#define PCH_DMA_CTL3 0x0C #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
#define PCH_DMA_STS2 0x18
#define dma_readl(pd, name) \ #define dma_readl(pd, name) \
readl((pd)->membase + PCH_DMA_##name) readl((pd)->membase + PCH_DMA_##name)
...@@ -183,13 +188,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable) ...@@ -183,13 +188,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable)
{ {
struct pch_dma *pd = to_pd(chan->device); struct pch_dma *pd = to_pd(chan->device);
u32 val; u32 val;
int pos;
if (chan->chan_id < 8)
pos = chan->chan_id;
else
pos = chan->chan_id + 8;
val = dma_readl(pd, CTL2); val = dma_readl(pd, CTL2);
if (enable) if (enable)
val |= 0x1 << chan->chan_id; val |= 0x1 << pos;
else else
val &= ~(0x1 << chan->chan_id); val &= ~(0x1 << pos);
dma_writel(pd, CTL2, val); dma_writel(pd, CTL2, val);
...@@ -202,10 +213,17 @@ static void pdc_set_dir(struct dma_chan *chan) ...@@ -202,10 +213,17 @@ static void pdc_set_dir(struct dma_chan *chan)
struct pch_dma_chan *pd_chan = to_pd_chan(chan); struct pch_dma_chan *pd_chan = to_pd_chan(chan);
struct pch_dma *pd = to_pd(chan->device); struct pch_dma *pd = to_pd(chan->device);
u32 val; u32 val;
u32 mask_mode;
u32 mask_ctl;
if (chan->chan_id < 8) { if (chan->chan_id < 8) {
val = dma_readl(pd, CTL0); val = dma_readl(pd, CTL0);
mask_mode = DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * chan->chan_id);
mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * chan->chan_id));
val &= mask_mode;
if (pd_chan->dir == DMA_TO_DEVICE) if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id + val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
DMA_CTL0_DIR_SHIFT_BITS); DMA_CTL0_DIR_SHIFT_BITS);
...@@ -213,18 +231,24 @@ static void pdc_set_dir(struct dma_chan *chan) ...@@ -213,18 +231,24 @@ static void pdc_set_dir(struct dma_chan *chan)
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id + val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
DMA_CTL0_DIR_SHIFT_BITS)); DMA_CTL0_DIR_SHIFT_BITS));
val |= mask_ctl;
dma_writel(pd, CTL0, val); dma_writel(pd, CTL0, val);
} else { } else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */ int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
val = dma_readl(pd, CTL3); val = dma_readl(pd, CTL3);
mask_mode = DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * ch);
mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * ch));
val &= mask_mode;
if (pd_chan->dir == DMA_TO_DEVICE) if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch + val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS); DMA_CTL0_DIR_SHIFT_BITS);
else else
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch + val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS)); DMA_CTL0_DIR_SHIFT_BITS));
val |= mask_ctl;
dma_writel(pd, CTL3, val); dma_writel(pd, CTL3, val);
} }
...@@ -236,33 +260,37 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode) ...@@ -236,33 +260,37 @@ 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;
u32 mask_ctl;
u32 mask_dir;
if (chan->chan_id < 8) { if (chan->chan_id < 8) {
mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * chan->chan_id));
mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +\
DMA_CTL0_DIR_SHIFT_BITS);
val = dma_readl(pd, CTL0); val = dma_readl(pd, CTL0);
val &= mask_dir;
val &= ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * chan->chan_id));
val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id); val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id);
val |= mask_ctl;
dma_writel(pd, CTL0, val); dma_writel(pd, CTL0, val);
} else { } else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */ int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * ch));
mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * ch +\
DMA_CTL0_DIR_SHIFT_BITS);
val = dma_readl(pd, CTL3); val = dma_readl(pd, CTL3);
val &= mask_dir;
val &= ~(DMA_CTL0_MODE_MASK_BITS <<
(DMA_CTL0_BITS_PER_CH * ch));
val |= mode << (DMA_CTL0_BITS_PER_CH * ch); val |= mode << (DMA_CTL0_BITS_PER_CH * ch);
val |= mask_ctl;
dma_writel(pd, CTL3, val); 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);
} }
static u32 pdc_get_status(struct pch_dma_chan *pd_chan) static u32 pdc_get_status0(struct pch_dma_chan *pd_chan)
{ {
struct pch_dma *pd = to_pd(pd_chan->chan.device); struct pch_dma *pd = to_pd(pd_chan->chan.device);
u32 val; u32 val;
...@@ -272,9 +300,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan) ...@@ -272,9 +300,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id)); DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id));
} }
static u32 pdc_get_status2(struct pch_dma_chan *pd_chan)
{
struct pch_dma *pd = to_pd(pd_chan->chan.device);
u32 val;
val = dma_readl(pd, STS2);
return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS +
DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8)));
}
static bool pdc_is_idle(struct pch_dma_chan *pd_chan) static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
{ {
if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE) u32 sts;
if (pd_chan->chan.chan_id < 8)
sts = pdc_get_status0(pd_chan);
else
sts = pdc_get_status2(pd_chan);
if (sts == DMA_STATUS_IDLE)
return true; return true;
else else
return false; return false;
...@@ -495,11 +541,11 @@ static int pd_alloc_chan_resources(struct dma_chan *chan) ...@@ -495,11 +541,11 @@ static int pd_alloc_chan_resources(struct dma_chan *chan)
list_add_tail(&desc->desc_node, &tmp_list); list_add_tail(&desc->desc_node, &tmp_list);
} }
spin_lock_bh(&pd_chan->lock); spin_lock_irq(&pd_chan->lock);
list_splice(&tmp_list, &pd_chan->free_list); list_splice(&tmp_list, &pd_chan->free_list);
pd_chan->descs_allocated = i; pd_chan->descs_allocated = i;
pd_chan->completed_cookie = chan->cookie = 1; pd_chan->completed_cookie = chan->cookie = 1;
spin_unlock_bh(&pd_chan->lock); spin_unlock_irq(&pd_chan->lock);
pdc_enable_irq(chan, 1); pdc_enable_irq(chan, 1);
...@@ -517,10 +563,10 @@ static void pd_free_chan_resources(struct dma_chan *chan) ...@@ -517,10 +563,10 @@ static void pd_free_chan_resources(struct dma_chan *chan)
BUG_ON(!list_empty(&pd_chan->active_list)); BUG_ON(!list_empty(&pd_chan->active_list));
BUG_ON(!list_empty(&pd_chan->queue)); BUG_ON(!list_empty(&pd_chan->queue));
spin_lock_bh(&pd_chan->lock); spin_lock_irq(&pd_chan->lock);
list_splice_init(&pd_chan->free_list, &tmp_list); list_splice_init(&pd_chan->free_list, &tmp_list);
pd_chan->descs_allocated = 0; pd_chan->descs_allocated = 0;
spin_unlock_bh(&pd_chan->lock); spin_unlock_irq(&pd_chan->lock);
list_for_each_entry_safe(desc, _d, &tmp_list, desc_node) list_for_each_entry_safe(desc, _d, &tmp_list, desc_node)
pci_pool_free(pd->pool, desc, desc->txd.phys); pci_pool_free(pd->pool, desc, desc->txd.phys);
...@@ -536,10 +582,10 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ...@@ -536,10 +582,10 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
dma_cookie_t last_completed; dma_cookie_t last_completed;
int ret; int ret;
spin_lock_bh(&pd_chan->lock); spin_lock_irq(&pd_chan->lock);
last_completed = pd_chan->completed_cookie; last_completed = pd_chan->completed_cookie;
last_used = chan->cookie; last_used = chan->cookie;
spin_unlock_bh(&pd_chan->lock); spin_unlock_irq(&pd_chan->lock);
ret = dma_async_is_complete(cookie, last_completed, last_used); ret = dma_async_is_complete(cookie, last_completed, last_used);
...@@ -654,7 +700,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -654,7 +700,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
if (cmd != DMA_TERMINATE_ALL) if (cmd != DMA_TERMINATE_ALL)
return -ENXIO; return -ENXIO;
spin_lock_bh(&pd_chan->lock); spin_lock_irq(&pd_chan->lock);
pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE); pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE);
...@@ -664,7 +710,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -664,7 +710,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
list_for_each_entry_safe(desc, _d, &list, desc_node) list_for_each_entry_safe(desc, _d, &list, desc_node)
pdc_chain_complete(pd_chan, desc); pdc_chain_complete(pd_chan, desc);
spin_unlock_bh(&pd_chan->lock); spin_unlock_irq(&pd_chan->lock);
return 0; return 0;
} }
...@@ -693,30 +739,45 @@ static irqreturn_t pd_irq(int irq, void *devid) ...@@ -693,30 +739,45 @@ static irqreturn_t pd_irq(int irq, void *devid)
struct pch_dma *pd = (struct pch_dma *)devid; struct pch_dma *pd = (struct pch_dma *)devid;
struct pch_dma_chan *pd_chan; struct pch_dma_chan *pd_chan;
u32 sts0; u32 sts0;
u32 sts2;
int i; int i;
int ret = IRQ_NONE; int ret0 = IRQ_NONE;
int ret2 = IRQ_NONE;
sts0 = dma_readl(pd, STS0); sts0 = dma_readl(pd, STS0);
sts2 = dma_readl(pd, STS2);
dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0); dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0);
for (i = 0; i < pd->dma.chancnt; i++) { for (i = 0; i < pd->dma.chancnt; i++) {
pd_chan = &pd->channels[i]; pd_chan = &pd->channels[i];
if (sts0 & DMA_STATUS_IRQ(i)) { if (i < 8) {
if (sts0 & DMA_STATUS_ERR(i)) if (sts0 & DMA_STATUS_IRQ(i)) {
set_bit(0, &pd_chan->err_status); if (sts0 & DMA_STATUS0_ERR(i))
set_bit(0, &pd_chan->err_status);
tasklet_schedule(&pd_chan->tasklet); tasklet_schedule(&pd_chan->tasklet);
ret = IRQ_HANDLED; ret0 = IRQ_HANDLED;
} }
} else {
if (sts2 & DMA_STATUS_IRQ(i - 8)) {
if (sts2 & DMA_STATUS2_ERR(i))
set_bit(0, &pd_chan->err_status);
tasklet_schedule(&pd_chan->tasklet);
ret2 = IRQ_HANDLED;
}
}
} }
/* clear interrupt bits in status register */ /* clear interrupt bits in status register */
dma_writel(pd, STS0, sts0); if (ret0)
dma_writel(pd, STS0, sts0);
if (ret2)
dma_writel(pd, STS2, sts2);
return ret; return ret0 | ret2;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
......
...@@ -82,7 +82,7 @@ struct dma_pl330_dmac { ...@@ -82,7 +82,7 @@ struct dma_pl330_dmac {
spinlock_t pool_lock; spinlock_t pool_lock;
/* Peripheral channels connected to this DMAC */ /* Peripheral channels connected to this DMAC */
struct dma_pl330_chan peripherals[0]; /* keep at end */ struct dma_pl330_chan *peripherals; /* keep at end */
}; };
struct dma_pl330_desc { struct dma_pl330_desc {
...@@ -451,8 +451,13 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) ...@@ -451,8 +451,13 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
desc->txd.cookie = 0; desc->txd.cookie = 0;
async_tx_ack(&desc->txd); async_tx_ack(&desc->txd);
desc->req.rqtype = peri->rqtype; if (peri) {
desc->req.peri = peri->peri_id; desc->req.rqtype = peri->rqtype;
desc->req.peri = peri->peri_id;
} else {
desc->req.rqtype = MEMTOMEM;
desc->req.peri = 0;
}
dma_async_tx_descriptor_init(&desc->txd, &pch->chan); dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
...@@ -529,10 +534,10 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, ...@@ -529,10 +534,10 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
struct pl330_info *pi; struct pl330_info *pi;
int burst; int burst;
if (unlikely(!pch || !len || !peri)) if (unlikely(!pch || !len))
return NULL; return NULL;
if (peri->rqtype != MEMTOMEM) if (peri && peri->rqtype != MEMTOMEM)
return NULL; return NULL;
pi = &pch->dmac->pif; pi = &pch->dmac->pif;
...@@ -577,7 +582,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -577,7 +582,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
int i, burst_size; int i, burst_size;
dma_addr_t addr; dma_addr_t addr;
if (unlikely(!pch || !sgl || !sg_len)) if (unlikely(!pch || !sgl || !sg_len || !peri))
return NULL; return NULL;
/* Make sure the direction is consistent */ /* Make sure the direction is consistent */
...@@ -666,17 +671,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -666,17 +671,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
struct dma_device *pd; struct dma_device *pd;
struct resource *res; struct resource *res;
int i, ret, irq; int i, ret, irq;
int num_chan;
pdat = adev->dev.platform_data; pdat = adev->dev.platform_data;
if (!pdat || !pdat->nr_valid_peri) {
dev_err(&adev->dev, "platform data missing\n");
return -ENODEV;
}
/* Allocate a new DMAC and its Channels */ /* Allocate a new DMAC and its Channels */
pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch) pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
+ sizeof(*pdmac), GFP_KERNEL);
if (!pdmac) { if (!pdmac) {
dev_err(&adev->dev, "unable to allocate mem\n"); dev_err(&adev->dev, "unable to allocate mem\n");
return -ENOMEM; return -ENOMEM;
...@@ -685,7 +685,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -685,7 +685,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pi = &pdmac->pif; pi = &pdmac->pif;
pi->dev = &adev->dev; pi->dev = &adev->dev;
pi->pl330_data = NULL; pi->pl330_data = NULL;
pi->mcbufsz = pdat->mcbuf_sz; pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
res = &adev->res; res = &adev->res;
request_mem_region(res->start, resource_size(res), "dma-pl330"); request_mem_region(res->start, resource_size(res), "dma-pl330");
...@@ -717,27 +717,35 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -717,27 +717,35 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
INIT_LIST_HEAD(&pd->channels); INIT_LIST_HEAD(&pd->channels);
/* Initialize channel parameters */ /* Initialize channel parameters */
for (i = 0; i < pdat->nr_valid_peri; i++) { num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan);
struct dma_pl330_peri *peri = &pdat->peri[i]; pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
pch = &pdmac->peripherals[i];
switch (peri->rqtype) { for (i = 0; i < num_chan; i++) {
case MEMTOMEM: pch = &pdmac->peripherals[i];
if (pdat) {
struct dma_pl330_peri *peri = &pdat->peri[i];
switch (peri->rqtype) {
case MEMTOMEM:
dma_cap_set(DMA_MEMCPY, pd->cap_mask);
break;
case MEMTODEV:
case DEVTOMEM:
dma_cap_set(DMA_SLAVE, pd->cap_mask);
break;
default:
dev_err(&adev->dev, "DEVTODEV Not Supported\n");
continue;
}
pch->chan.private = peri;
} else {
dma_cap_set(DMA_MEMCPY, pd->cap_mask); dma_cap_set(DMA_MEMCPY, pd->cap_mask);
break; pch->chan.private = NULL;
case MEMTODEV:
case DEVTOMEM:
dma_cap_set(DMA_SLAVE, pd->cap_mask);
break;
default:
dev_err(&adev->dev, "DEVTODEV Not Supported\n");
continue;
} }
INIT_LIST_HEAD(&pch->work_list); INIT_LIST_HEAD(&pch->work_list);
spin_lock_init(&pch->lock); spin_lock_init(&pch->lock);
pch->pl330_chid = NULL; pch->pl330_chid = NULL;
pch->chan.private = peri;
pch->chan.device = pd; pch->chan.device = pd;
pch->chan.chan_id = i; pch->chan.chan_id = i;
pch->dmac = pdmac; pch->dmac = pdmac;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/amba/bus.h>
#include <plat/ste_dma40.h> #include <plat/ste_dma40.h>
...@@ -45,9 +46,6 @@ ...@@ -45,9 +46,6 @@
#define D40_ALLOC_PHY (1 << 30) #define D40_ALLOC_PHY (1 << 30)
#define D40_ALLOC_LOG_FREE 0 #define D40_ALLOC_LOG_FREE 0
/* Hardware designer of the block */
#define D40_HW_DESIGNER 0x8
/** /**
* enum 40_command - The different commands and/or statuses. * enum 40_command - The different commands and/or statuses.
* *
...@@ -186,6 +184,8 @@ struct d40_base; ...@@ -186,6 +184,8 @@ struct d40_base;
* @log_def: Default logical channel settings. * @log_def: Default logical channel settings.
* @lcla: Space for one dst src pair for logical channel transfers. * @lcla: Space for one dst src pair for logical channel transfers.
* @lcpa: Pointer to dst and src lcpa settings. * @lcpa: Pointer to dst and src lcpa settings.
* @runtime_addr: runtime configured address.
* @runtime_direction: runtime configured direction.
* *
* This struct can either "be" a logical or a physical channel. * This struct can either "be" a logical or a physical channel.
*/ */
...@@ -200,6 +200,7 @@ struct d40_chan { ...@@ -200,6 +200,7 @@ struct d40_chan {
struct dma_chan chan; struct dma_chan chan;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
struct list_head client; struct list_head client;
struct list_head pending_queue;
struct list_head active; struct list_head active;
struct list_head queue; struct list_head queue;
struct stedma40_chan_cfg dma_cfg; struct stedma40_chan_cfg dma_cfg;
...@@ -645,7 +646,20 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c) ...@@ -645,7 +646,20 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc) static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
{ {
list_add_tail(&desc->node, &d40c->queue); list_add_tail(&desc->node, &d40c->pending_queue);
}
static struct d40_desc *d40_first_pending(struct d40_chan *d40c)
{
struct d40_desc *d;
if (list_empty(&d40c->pending_queue))
return NULL;
d = list_first_entry(&d40c->pending_queue,
struct d40_desc,
node);
return d;
} }
static struct d40_desc *d40_first_queued(struct d40_chan *d40c) static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
...@@ -802,6 +816,11 @@ static void d40_term_all(struct d40_chan *d40c) ...@@ -802,6 +816,11 @@ static void d40_term_all(struct d40_chan *d40c)
d40_desc_free(d40c, d40d); d40_desc_free(d40c, d40d);
} }
/* Release pending descriptors */
while ((d40d = d40_first_pending(d40c))) {
d40_desc_remove(d40d);
d40_desc_free(d40c, d40d);
}
d40c->pending_tx = 0; d40c->pending_tx = 0;
d40c->busy = false; d40c->busy = false;
...@@ -2092,7 +2111,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, ...@@ -2092,7 +2111,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
struct scatterlist *sg; struct scatterlist *sg;
int i; int i;
sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_KERNEL); sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT);
for (i = 0; i < periods; i++) { for (i = 0; i < periods; i++) {
sg_dma_address(&sg[i]) = dma_addr; sg_dma_address(&sg[i]) = dma_addr;
sg_dma_len(&sg[i]) = period_len; sg_dma_len(&sg[i]) = period_len;
...@@ -2152,24 +2171,87 @@ static void d40_issue_pending(struct dma_chan *chan) ...@@ -2152,24 +2171,87 @@ static void d40_issue_pending(struct dma_chan *chan)
spin_lock_irqsave(&d40c->lock, flags); spin_lock_irqsave(&d40c->lock, flags);
/* Busy means that pending jobs are already being processed */ list_splice_tail_init(&d40c->pending_queue, &d40c->queue);
/* Busy means that queued jobs are already being processed */
if (!d40c->busy) if (!d40c->busy)
(void) d40_queue_start(d40c); (void) d40_queue_start(d40c);
spin_unlock_irqrestore(&d40c->lock, flags); spin_unlock_irqrestore(&d40c->lock, flags);
} }
static int
dma40_config_to_halfchannel(struct d40_chan *d40c,
struct stedma40_half_channel_info *info,
enum dma_slave_buswidth width,
u32 maxburst)
{
enum stedma40_periph_data_width addr_width;
int psize;
switch (width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
addr_width = STEDMA40_BYTE_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
addr_width = STEDMA40_HALFWORD_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
addr_width = STEDMA40_WORD_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_8_BYTES:
addr_width = STEDMA40_DOUBLEWORD_WIDTH;
break;
default:
dev_err(d40c->base->dev,
"illegal peripheral address width "
"requested (%d)\n",
width);
return -EINVAL;
}
if (chan_is_logical(d40c)) {
if (maxburst >= 16)
psize = STEDMA40_PSIZE_LOG_16;
else if (maxburst >= 8)
psize = STEDMA40_PSIZE_LOG_8;
else if (maxburst >= 4)
psize = STEDMA40_PSIZE_LOG_4;
else
psize = STEDMA40_PSIZE_LOG_1;
} else {
if (maxburst >= 16)
psize = STEDMA40_PSIZE_PHY_16;
else if (maxburst >= 8)
psize = STEDMA40_PSIZE_PHY_8;
else if (maxburst >= 4)
psize = STEDMA40_PSIZE_PHY_4;
else
psize = STEDMA40_PSIZE_PHY_1;
}
info->data_width = addr_width;
info->psize = psize;
info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
return 0;
}
/* Runtime reconfiguration extension */ /* Runtime reconfiguration extension */
static void d40_set_runtime_config(struct dma_chan *chan, static int d40_set_runtime_config(struct dma_chan *chan,
struct dma_slave_config *config) struct dma_slave_config *config)
{ {
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
struct stedma40_chan_cfg *cfg = &d40c->dma_cfg; struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
enum dma_slave_buswidth config_addr_width; enum dma_slave_buswidth src_addr_width, dst_addr_width;
dma_addr_t config_addr; dma_addr_t config_addr;
u32 config_maxburst; u32 src_maxburst, dst_maxburst;
enum stedma40_periph_data_width addr_width; int ret;
int psize;
src_addr_width = config->src_addr_width;
src_maxburst = config->src_maxburst;
dst_addr_width = config->dst_addr_width;
dst_maxburst = config->dst_maxburst;
if (config->direction == DMA_FROM_DEVICE) { if (config->direction == DMA_FROM_DEVICE) {
dma_addr_t dev_addr_rx = dma_addr_t dev_addr_rx =
...@@ -2188,8 +2270,11 @@ static void d40_set_runtime_config(struct dma_chan *chan, ...@@ -2188,8 +2270,11 @@ static void d40_set_runtime_config(struct dma_chan *chan,
cfg->dir); cfg->dir);
cfg->dir = STEDMA40_PERIPH_TO_MEM; cfg->dir = STEDMA40_PERIPH_TO_MEM;
config_addr_width = config->src_addr_width; /* Configure the memory side */
config_maxburst = config->src_maxburst; if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
dst_addr_width = src_addr_width;
if (dst_maxburst == 0)
dst_maxburst = src_maxburst;
} else if (config->direction == DMA_TO_DEVICE) { } else if (config->direction == DMA_TO_DEVICE) {
dma_addr_t dev_addr_tx = dma_addr_t dev_addr_tx =
...@@ -2208,68 +2293,39 @@ static void d40_set_runtime_config(struct dma_chan *chan, ...@@ -2208,68 +2293,39 @@ static void d40_set_runtime_config(struct dma_chan *chan,
cfg->dir); cfg->dir);
cfg->dir = STEDMA40_MEM_TO_PERIPH; cfg->dir = STEDMA40_MEM_TO_PERIPH;
config_addr_width = config->dst_addr_width; /* Configure the memory side */
config_maxburst = config->dst_maxburst; if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
src_addr_width = dst_addr_width;
if (src_maxburst == 0)
src_maxburst = dst_maxburst;
} else { } else {
dev_err(d40c->base->dev, dev_err(d40c->base->dev,
"unrecognized channel direction %d\n", "unrecognized channel direction %d\n",
config->direction); config->direction);
return; return -EINVAL;
} }
switch (config_addr_width) { if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
addr_width = STEDMA40_BYTE_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
addr_width = STEDMA40_HALFWORD_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
addr_width = STEDMA40_WORD_WIDTH;
break;
case DMA_SLAVE_BUSWIDTH_8_BYTES:
addr_width = STEDMA40_DOUBLEWORD_WIDTH;
break;
default:
dev_err(d40c->base->dev, dev_err(d40c->base->dev,
"illegal peripheral address width " "src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
"requested (%d)\n", src_maxburst,
config->src_addr_width); src_addr_width,
return; dst_maxburst,
dst_addr_width);
return -EINVAL;
} }
if (chan_is_logical(d40c)) { ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
if (config_maxburst >= 16) src_addr_width,
psize = STEDMA40_PSIZE_LOG_16; src_maxburst);
else if (config_maxburst >= 8) if (ret)
psize = STEDMA40_PSIZE_LOG_8; return ret;
else if (config_maxburst >= 4)
psize = STEDMA40_PSIZE_LOG_4;
else
psize = STEDMA40_PSIZE_LOG_1;
} else {
if (config_maxburst >= 16)
psize = STEDMA40_PSIZE_PHY_16;
else if (config_maxburst >= 8)
psize = STEDMA40_PSIZE_PHY_8;
else if (config_maxburst >= 4)
psize = STEDMA40_PSIZE_PHY_4;
else if (config_maxburst >= 2)
psize = STEDMA40_PSIZE_PHY_2;
else
psize = STEDMA40_PSIZE_PHY_1;
}
/* Set up all the endpoint configs */ ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
cfg->src_info.data_width = addr_width; dst_addr_width,
cfg->src_info.psize = psize; dst_maxburst);
cfg->src_info.big_endian = false; if (ret)
cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL; return ret;
cfg->dst_info.data_width = addr_width;
cfg->dst_info.psize = psize;
cfg->dst_info.big_endian = false;
cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
/* Fill in register values */ /* Fill in register values */
if (chan_is_logical(d40c)) if (chan_is_logical(d40c))
...@@ -2282,12 +2338,14 @@ static void d40_set_runtime_config(struct dma_chan *chan, ...@@ -2282,12 +2338,14 @@ static void d40_set_runtime_config(struct dma_chan *chan,
d40c->runtime_addr = config_addr; d40c->runtime_addr = config_addr;
d40c->runtime_direction = config->direction; d40c->runtime_direction = config->direction;
dev_dbg(d40c->base->dev, dev_dbg(d40c->base->dev,
"configured channel %s for %s, data width %d, " "configured channel %s for %s, data width %d/%d, "
"maxburst %d bytes, LE, no flow control\n", "maxburst %d/%d elements, LE, no flow control\n",
dma_chan_name(chan), dma_chan_name(chan),
(config->direction == DMA_FROM_DEVICE) ? "RX" : "TX", (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
config_addr_width, src_addr_width, dst_addr_width,
config_maxburst); src_maxburst, dst_maxburst);
return 0;
} }
static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
...@@ -2308,9 +2366,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -2308,9 +2366,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
case DMA_RESUME: case DMA_RESUME:
return d40_resume(d40c); return d40_resume(d40c);
case DMA_SLAVE_CONFIG: case DMA_SLAVE_CONFIG:
d40_set_runtime_config(chan, return d40_set_runtime_config(chan,
(struct dma_slave_config *) arg); (struct dma_slave_config *) arg);
return 0;
default: default:
break; break;
} }
...@@ -2341,6 +2398,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, ...@@ -2341,6 +2398,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
INIT_LIST_HEAD(&d40c->active); INIT_LIST_HEAD(&d40c->active);
INIT_LIST_HEAD(&d40c->queue); INIT_LIST_HEAD(&d40c->queue);
INIT_LIST_HEAD(&d40c->pending_queue);
INIT_LIST_HEAD(&d40c->client); INIT_LIST_HEAD(&d40c->client);
tasklet_init(&d40c->tasklet, dma_tasklet, tasklet_init(&d40c->tasklet, dma_tasklet,
...@@ -2502,25 +2560,6 @@ static int __init d40_phy_res_init(struct d40_base *base) ...@@ -2502,25 +2560,6 @@ static int __init d40_phy_res_init(struct d40_base *base)
static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
{ {
static const struct d40_reg_val dma_id_regs[] = {
/* Peripheral Id */
{ .reg = D40_DREG_PERIPHID0, .val = 0x0040},
{ .reg = D40_DREG_PERIPHID1, .val = 0x0000},
/*
* D40_DREG_PERIPHID2 Depends on HW revision:
* DB8500ed has 0x0008,
* ? has 0x0018,
* DB8500v1 has 0x0028
* DB8500v2 has 0x0038
*/
{ .reg = D40_DREG_PERIPHID3, .val = 0x0000},
/* PCell Id */
{ .reg = D40_DREG_CELLID0, .val = 0x000d},
{ .reg = D40_DREG_CELLID1, .val = 0x00f0},
{ .reg = D40_DREG_CELLID2, .val = 0x0005},
{ .reg = D40_DREG_CELLID3, .val = 0x00b1}
};
struct stedma40_platform_data *plat_data; struct stedma40_platform_data *plat_data;
struct clk *clk = NULL; struct clk *clk = NULL;
void __iomem *virtbase = NULL; void __iomem *virtbase = NULL;
...@@ -2529,8 +2568,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) ...@@ -2529,8 +2568,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
int num_log_chans = 0; int num_log_chans = 0;
int num_phy_chans; int num_phy_chans;
int i; int i;
u32 val; u32 pid;
u32 rev; u32 cid;
u8 rev;
clk = clk_get(&pdev->dev, NULL); clk = clk_get(&pdev->dev, NULL);
...@@ -2554,32 +2594,32 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) ...@@ -2554,32 +2594,32 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
if (!virtbase) if (!virtbase)
goto failure; goto failure;
/* HW version check */ /* This is just a regular AMBA PrimeCell ID actually */
for (i = 0; i < ARRAY_SIZE(dma_id_regs); i++) { for (pid = 0, i = 0; i < 4; i++)
if (dma_id_regs[i].val != pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i)
readl(virtbase + dma_id_regs[i].reg)) { & 255) << (i * 8);
d40_err(&pdev->dev, for (cid = 0, i = 0; i < 4; i++)
"Unknown hardware! Expected 0x%x at 0x%x but got 0x%x\n", cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i)
dma_id_regs[i].val, & 255) << (i * 8);
dma_id_regs[i].reg,
readl(virtbase + dma_id_regs[i].reg));
goto failure;
}
}
/* Get silicon revision and designer */ if (cid != AMBA_CID) {
val = readl(virtbase + D40_DREG_PERIPHID2); d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n");
goto failure;
if ((val & D40_DREG_PERIPHID2_DESIGNER_MASK) != }
D40_HW_DESIGNER) { if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) {
d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n", d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n",
val & D40_DREG_PERIPHID2_DESIGNER_MASK, AMBA_MANF_BITS(pid),
D40_HW_DESIGNER); AMBA_VENDOR_ST);
goto failure; goto failure;
} }
/*
rev = (val & D40_DREG_PERIPHID2_REV_MASK) >> * HW revision:
D40_DREG_PERIPHID2_REV_POS; * DB8500ed has revision 0
* ? has revision 1
* DB8500v1 has revision 2
* DB8500v2 has revision 3
*/
rev = AMBA_REV_BITS(pid);
/* The number of physical channels on this HW */ /* The number of physical channels on this HW */
num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
......
...@@ -184,9 +184,6 @@ ...@@ -184,9 +184,6 @@
#define D40_DREG_PERIPHID0 0xFE0 #define D40_DREG_PERIPHID0 0xFE0
#define D40_DREG_PERIPHID1 0xFE4 #define D40_DREG_PERIPHID1 0xFE4
#define D40_DREG_PERIPHID2 0xFE8 #define D40_DREG_PERIPHID2 0xFE8
#define D40_DREG_PERIPHID2_REV_POS 4
#define D40_DREG_PERIPHID2_REV_MASK (0xf << D40_DREG_PERIPHID2_REV_POS)
#define D40_DREG_PERIPHID2_DESIGNER_MASK 0xf
#define D40_DREG_PERIPHID3 0xFEC #define D40_DREG_PERIPHID3 0xFEC
#define D40_DREG_CELLID0 0xFF0 #define D40_DREG_CELLID0 0xFF0
#define D40_DREG_CELLID1 0xFF4 #define D40_DREG_CELLID1 0xFF4
......
...@@ -172,8 +172,11 @@ struct pl08x_dma_chan { ...@@ -172,8 +172,11 @@ struct pl08x_dma_chan {
int phychan_hold; int phychan_hold;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
char *name; char *name;
struct pl08x_channel_data *cd; const struct pl08x_channel_data *cd;
dma_addr_t runtime_addr; dma_addr_t src_addr;
dma_addr_t dst_addr;
u32 src_cctl;
u32 dst_cctl;
enum dma_data_direction runtime_direction; enum dma_data_direction runtime_direction;
dma_cookie_t lc; dma_cookie_t lc;
struct list_head pend_list; struct list_head pend_list;
...@@ -202,7 +205,7 @@ struct pl08x_dma_chan { ...@@ -202,7 +205,7 @@ struct pl08x_dma_chan {
* @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2 * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2
*/ */
struct pl08x_platform_data { struct pl08x_platform_data {
struct pl08x_channel_data *slave_channels; const struct pl08x_channel_data *slave_channels;
unsigned int num_slave_channels; unsigned int num_slave_channels;
struct pl08x_channel_data memcpy_channel; struct pl08x_channel_data memcpy_channel;
int (*get_signal)(struct pl08x_dma_chan *); int (*get_signal)(struct pl08x_dma_chan *);
......
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