Skip to content

Commit

Permalink
dmaengine: PL08x: move DMA signal muxing into slave prepare code
Browse files Browse the repository at this point in the history
Move the DMA request muxing into the slave prepare code and txd
release/completion code.  This means we only hold the DMA request
mux while there are descriptors waiting to be started or are in
progress.

This leaves txd->direction as a write-only variable; remove it.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Russell King committed Jul 1, 2012
1 parent a936e79 commit c48d496
Showing 1 changed file with 32 additions and 47 deletions.
79 changes: 32 additions & 47 deletions drivers/dma/amba-pl08x.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ struct pl08x_sg {
* @tx: async tx descriptor
* @node: node for txd list for channels
* @dsg_list: list of children sg's
* @direction: direction of transfer
* @llis_bus: DMA memory address (physical) start for the LLIs
* @llis_va: virtual memory address start for the LLIs
* @cctl: control reg values for current txd
Expand All @@ -178,7 +177,6 @@ struct pl08x_txd {
struct dma_async_tx_descriptor tx;
struct list_head node;
struct list_head dsg_list;
enum dma_transfer_direction direction;
dma_addr_t llis_bus;
struct pl08x_lli *llis_va;
/* Default cctl value for LLIs */
Expand Down Expand Up @@ -997,6 +995,7 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
if (!list_empty(&plchan->pend_list)) {
list_for_each_entry_safe(txdi,
next, &plchan->pend_list, node) {
pl08x_release_mux(plchan);
list_del(&txdi->node);
pl08x_free_txd(pl08x, txdi);
}
Expand All @@ -1018,12 +1017,10 @@ static void pl08x_free_chan_resources(struct dma_chan *chan)
/*
* This should be called with the channel plchan->lock held
*/
static int prep_phy_channel(struct pl08x_dma_chan *plchan,
struct pl08x_txd *txd)
static int prep_phy_channel(struct pl08x_dma_chan *plchan)
{
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_phy_chan *ch;
int ret;

/* Check if we already have a channel */
if (plchan->phychan) {
Expand All @@ -1038,36 +1035,11 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
return -EBUSY;
}

/*
* OK we have a physical channel: for memcpy() this is all we
* need, but for slaves the physical signals may be muxed!
* Can the platform allow us to use this channel?
*/
if (plchan->slave) {
ret = pl08x_request_mux(plchan);
if (ret < 0) {
dev_dbg(&pl08x->adev->dev,
"unable to use physical channel %d for transfer on %s due to platform restrictions\n",
ch->id, plchan->name);
/* Release physical channel & return */
pl08x_put_phy_channel(pl08x, ch);
return -EBUSY;
}
}

plchan->phychan = ch;
dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n",
ch->id,
plchan->signal,
plchan->name);
dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n",
ch->id, plchan->name);

got_channel:
/* Assign the flow control signal to this channel */
if (txd->direction == DMA_MEM_TO_DEV)
txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT;
else if (txd->direction == DMA_DEV_TO_MEM)
txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;

plchan->phychan_hold++;

return 0;
Expand All @@ -1077,7 +1049,6 @@ static void release_phy_channel(struct pl08x_dma_chan *plchan)
{
struct pl08x_driver_data *pl08x = plchan->host;

pl08x_release_mux(plchan);
pl08x_put_phy_channel(pl08x, plchan->phychan);
plchan->phychan = NULL;
}
Expand Down Expand Up @@ -1340,19 +1311,12 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
* See if we already have a physical channel allocated,
* else this is the time to try to get one.
*/
ret = prep_phy_channel(plchan, txd);
ret = prep_phy_channel(plchan);
if (ret) {
/*
* No physical channel was available.
*
* memcpy transfers can be sorted out at submission time.
*
* Slave transfers may have been denied due to platform
* channel muxing restrictions. Since there is no guarantee
* that this will ever be resolved, and the signal must be
* acquired AFTER acquiring the physical channel, we will let
* them be NACK:ed with -EBUSY here. The drivers can retry
* the prep() call if they are eager on doing this using DMA.
*/
if (plchan->slave) {
pl08x_free_txd_list(pl08x, plchan);
Expand Down Expand Up @@ -1423,7 +1387,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
}
list_add_tail(&dsg->node, &txd->dsg_list);

txd->direction = DMA_MEM_TO_MEM;
dsg->src_addr = src;
dsg->dst_addr = dest;
dsg->len = len;
Expand Down Expand Up @@ -1477,8 +1440,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
* will take precedence since this may configure the
* channel target address dynamically at runtime.
*/
txd->direction = direction;

if (direction == DMA_MEM_TO_DEV) {
cctl = PL080_CONTROL_SRC_INCR;
slave_addr = plchan->cfg.dst_addr;
Expand Down Expand Up @@ -1519,9 +1480,28 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(

txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;

ret = pl08x_request_mux(plchan);
if (ret < 0) {
pl08x_free_txd(pl08x, txd);
dev_dbg(&pl08x->adev->dev,
"unable to mux for transfer on %s due to platform restrictions\n",
plchan->name);
return NULL;
}

dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n",
plchan->signal, plchan->name);

/* Assign the flow control signal to this channel */
if (direction == DMA_MEM_TO_DEV)
txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT;
else
txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;

for_each_sg(sgl, sg, sg_len, tmp) {
dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
if (!dsg) {
pl08x_release_mux(plchan);
pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
__func__);
Expand Down Expand Up @@ -1586,6 +1566,8 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
}
/* Dequeue jobs and free LLIs */
if (plchan->at) {
/* Killing this one off, release its mux */
pl08x_release_mux(plchan);
pl08x_free_txd(pl08x, plchan->at);
plchan->at = NULL;
}
Expand Down Expand Up @@ -1702,7 +1684,6 @@ static void pl08x_tasklet(unsigned long data)

/*
* No more jobs, so free up the physical channel
* Free any allocated signal on slave transfers too
*/
release_phy_channel(plchan);
plchan->state = PL08X_CHAN_IDLE;
Expand All @@ -1720,8 +1701,7 @@ static void pl08x_tasklet(unsigned long data)
int ret;

/* This should REALLY not fail now */
ret = prep_phy_channel(waiting,
waiting->waiting);
ret = prep_phy_channel(waiting);
BUG_ON(ret);
waiting->phychan_hold--;
waiting->state = PL08X_CHAN_RUNNING;
Expand Down Expand Up @@ -1794,6 +1774,11 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
tx = plchan->at;
if (tx) {
plchan->at = NULL;
/*
* This descriptor is done, release its mux
* reservation.
*/
pl08x_release_mux(plchan);
dma_cookie_complete(&tx->tx);
list_add_tail(&tx->node, &plchan->done_list);
}
Expand Down

0 comments on commit c48d496

Please sign in to comment.