提交 c5f47990 编写于 作者: J Joel Fernandes 提交者: Vinod Koul

dma: edma: Find missed events and issue them

In an effort to move to using Scatter gather lists of any size with
EDMA as discussed at [1] instead of placing limitations on the driver,
we work through the limitations of the EDMAC hardware to find missed
events and issue them.

The sequence of events that require this are:

For the scenario where MAX slots for an EDMA channel is 3:

SG1 -> SG2 -> SG3 -> SG4 -> SG5 -> SG6 -> Null

The above SG list will have to be DMA'd in 2 sets:

(1) SG1 -> SG2 -> SG3 -> Null
(2) SG4 -> SG5 -> SG6 -> Null

After (1) is succesfully transferred, the events from the MMC controller
donot stop coming and are missed by the time we have setup the transfer
for (2). So here, we catch the events missed as an error condition and
issue them manually.

In the second part of the patch, we make handle the NULL slot cases:
For crypto IP, we continue to receive events even continuously in
NULL slot, the setup of the next set of SG elements happens after
the error handler executes. This is results in some recursion problems.
Due to this, we continously receive error interrupts when we manually
trigger an event from the error handler.

We fix this, by first detecting if the Channel is currently transferring
from a NULL slot or not, that's where the edma_read_slot in the error
callback from interrupt handler comes in. With this we can determine if
the set up of the next SG list has completed, and we manually trigger
only in this case. If the setup has _not_ completed, we are still in NULL
so we just set a missed flag and allow the manual triggerring to happen
in edma_execute which will be eventually called. This fixes the above
mentioned race conditions seen with the crypto drivers.

[1] http://marc.info/?l=linux-omap&m=137416733628831&w=2Signed-off-by: NJoel Fernandes <joelf@ti.com>
Signed-off-by: NVinod Koul <vinod.koul@intel.com>
上级 96874b9a
...@@ -70,6 +70,7 @@ struct edma_chan { ...@@ -70,6 +70,7 @@ struct edma_chan {
int ch_num; int ch_num;
bool alloced; bool alloced;
int slot[EDMA_MAX_SLOTS]; int slot[EDMA_MAX_SLOTS];
int missed;
struct dma_slave_config cfg; struct dma_slave_config cfg;
}; };
...@@ -170,6 +171,20 @@ static void edma_execute(struct edma_chan *echan) ...@@ -170,6 +171,20 @@ static void edma_execute(struct edma_chan *echan)
dev_dbg(dev, "first transfer starting %d\n", echan->ch_num); dev_dbg(dev, "first transfer starting %d\n", echan->ch_num);
edma_start(echan->ch_num); edma_start(echan->ch_num);
} }
/*
* This happens due to setup times between intermediate transfers
* in long SG lists which have to be broken up into transfers of
* MAX_NR_SG
*/
if (echan->missed) {
dev_dbg(dev, "missed event in execute detected\n");
edma_clean_channel(echan->ch_num);
edma_stop(echan->ch_num);
edma_start(echan->ch_num);
edma_trigger_channel(echan->ch_num);
echan->missed = 0;
}
} }
static int edma_terminate_all(struct edma_chan *echan) static int edma_terminate_all(struct edma_chan *echan)
...@@ -387,6 +402,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) ...@@ -387,6 +402,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data)
struct device *dev = echan->vchan.chan.device->dev; struct device *dev = echan->vchan.chan.device->dev;
struct edma_desc *edesc; struct edma_desc *edesc;
unsigned long flags; unsigned long flags;
struct edmacc_param p;
/* Pause the channel */ /* Pause the channel */
edma_pause(echan->ch_num); edma_pause(echan->ch_num);
...@@ -412,7 +428,39 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) ...@@ -412,7 +428,39 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data)
break; break;
case DMA_CC_ERROR: case DMA_CC_ERROR:
dev_dbg(dev, "transfer error on channel %d\n", ch_num); spin_lock_irqsave(&echan->vchan.lock, flags);
edma_read_slot(EDMA_CHAN_SLOT(echan->slot[0]), &p);
/*
* Issue later based on missed flag which will be sure
* to happen as:
* (1) we finished transmitting an intermediate slot and
* edma_execute is coming up.
* (2) or we finished current transfer and issue will
* call edma_execute.
*
* Important note: issuing can be dangerous here and
* lead to some nasty recursion when we are in a NULL
* slot. So we avoid doing so and set the missed flag.
*/
if (p.a_b_cnt == 0 && p.ccnt == 0) {
dev_dbg(dev, "Error occurred, looks like slot is null, just setting miss\n");
echan->missed = 1;
} else {
/*
* The slot is already programmed but the event got
* missed, so its safe to issue it here.
*/
dev_dbg(dev, "Error occurred but slot is non-null, TRIGGERING\n");
edma_clean_channel(echan->ch_num);
edma_stop(echan->ch_num);
edma_start(echan->ch_num);
edma_trigger_channel(echan->ch_num);
}
spin_unlock_irqrestore(&echan->vchan.lock, flags);
break; break;
default: default:
break; break;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册