diff mbox

[v2,1/3] dma: mxs-dma: Cleanup interrupt handler

Message ID 1380546822-14034-2-git-send-email-mpa@pengutronix.de (mailing list archive)
State New, archived
Headers show

Commit Message

Markus Pargmann Sept. 30, 2013, 1:13 p.m. UTC
The DMA interrupt handler uses its controll registers to handle all
available channel interrupts it can find.

This patch changes it to handle only one interrupt by directly mapping
irq number to channel. It also includes a cleanup of the ctrl-register
usage.

Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 drivers/dma/mxs-dma.c | 95 ++++++++++++++++++++++++++++++++-------------------
 1 file changed, 59 insertions(+), 36 deletions(-)

Comments

Lothar Waßmann Sept. 30, 2013, 1:54 p.m. UTC | #1
Hi,

Markus Pargmann writes:
> The DMA interrupt handler uses its controll registers to handle all
> available channel interrupts it can find.
> 
> This patch changes it to handle only one interrupt by directly mapping
> irq number to channel. It also includes a cleanup of the ctrl-register
> usage.
> 
> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> ---
>  drivers/dma/mxs-dma.c | 95 ++++++++++++++++++++++++++++++++-------------------
>  1 file changed, 59 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
> index ccd13df..bfca8dc 100644
> --- a/drivers/dma/mxs-dma.c
> +++ b/drivers/dma/mxs-dma.c
> @@ -272,58 +272,81 @@ static void mxs_dma_tasklet(unsigned long data)
>  		mxs_chan->desc.callback(mxs_chan->desc.callback_param);
>  }
>  
> +static int mxs_dma_irq_to_chan(struct mxs_dma_engine *mxs_dma, int irq)
> +{
> +	int i;
> +
> +	for (i = 0; i != mxs_dma->nr_channels; ++i)
> +		if (mxs_dma->mxs_chans[i].chan_irq == irq)
> +			return i;
>
You might use a linked list for all active channels so that you don't
have to check all unused channels when trying to find the channel
number for an irq.


Lothar Waßmann
Marc Kleine-Budde Sept. 30, 2013, 1:59 p.m. UTC | #2
On 09/30/2013 03:54 PM, Lothar Waßmann wrote:
> Hi,
> 
> Markus Pargmann writes:
>> The DMA interrupt handler uses its controll registers to handle all
>> available channel interrupts it can find.
>>
>> This patch changes it to handle only one interrupt by directly mapping
>> irq number to channel. It also includes a cleanup of the ctrl-register
>> usage.
>>
>> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
>> ---
>>  drivers/dma/mxs-dma.c | 95 ++++++++++++++++++++++++++++++++-------------------
>>  1 file changed, 59 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
>> index ccd13df..bfca8dc 100644
>> --- a/drivers/dma/mxs-dma.c
>> +++ b/drivers/dma/mxs-dma.c
>> @@ -272,58 +272,81 @@ static void mxs_dma_tasklet(unsigned long data)
>>  		mxs_chan->desc.callback(mxs_chan->desc.callback_param);
>>  }
>>  
>> +static int mxs_dma_irq_to_chan(struct mxs_dma_engine *mxs_dma, int irq)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i != mxs_dma->nr_channels; ++i)
>> +		if (mxs_dma->mxs_chans[i].chan_irq == irq)
>> +			return i;
>>
> You might use a linked list for all active channels so that you don't
> have to check all unused channels when trying to find the channel
> number for an irq.

Or an array of mxs_dma->nr_channels length for direct mapping.

Marc
Markus Pargmann Sept. 30, 2013, 2:30 p.m. UTC | #3
Hi,

On Mon, Sep 30, 2013 at 03:59:10PM +0200, Marc Kleine-Budde wrote:
> On 09/30/2013 03:54 PM, Lothar Waßmann wrote:
> > Hi,
> > 
> > Markus Pargmann writes:
> >> The DMA interrupt handler uses its controll registers to handle all
> >> available channel interrupts it can find.
> >>
> >> This patch changes it to handle only one interrupt by directly mapping
> >> irq number to channel. It also includes a cleanup of the ctrl-register
> >> usage.
> >>
> >> Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> >> ---
> >>  drivers/dma/mxs-dma.c | 95 ++++++++++++++++++++++++++++++++-------------------
> >>  1 file changed, 59 insertions(+), 36 deletions(-)
> >>
> >> diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
> >> index ccd13df..bfca8dc 100644
> >> --- a/drivers/dma/mxs-dma.c
> >> +++ b/drivers/dma/mxs-dma.c
> >> @@ -272,58 +272,81 @@ static void mxs_dma_tasklet(unsigned long data)
> >>  		mxs_chan->desc.callback(mxs_chan->desc.callback_param);
> >>  }
> >>  
> >> +static int mxs_dma_irq_to_chan(struct mxs_dma_engine *mxs_dma, int irq)
> >> +{
> >> +	int i;
> >> +
> >> +	for (i = 0; i != mxs_dma->nr_channels; ++i)
> >> +		if (mxs_dma->mxs_chans[i].chan_irq == irq)
> >> +			return i;
> >>
> > You might use a linked list for all active channels so that you don't
> > have to check all unused channels when trying to find the channel
> > number for an irq.
> 
> Or an array of mxs_dma->nr_channels length for direct mapping.

We map irq to channel so an array of nr_channels won't work here.

I will use a linked list.

Thanks

Markus
diff mbox

Patch

diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index ccd13df..bfca8dc 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -272,58 +272,81 @@  static void mxs_dma_tasklet(unsigned long data)
 		mxs_chan->desc.callback(mxs_chan->desc.callback_param);
 }
 
+static int mxs_dma_irq_to_chan(struct mxs_dma_engine *mxs_dma, int irq)
+{
+	int i;
+
+	for (i = 0; i != mxs_dma->nr_channels; ++i)
+		if (mxs_dma->mxs_chans[i].chan_irq == irq)
+			return i;
+
+	return -EINVAL;
+}
+
 static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
 {
 	struct mxs_dma_engine *mxs_dma = dev_id;
-	u32 stat1, stat2;
+	struct mxs_dma_chan *mxs_chan;
+	u32 completed;
+	u32 err;
+	int chan = mxs_dma_irq_to_chan(mxs_dma, irq);
+
+	if (chan < 0)
+		return IRQ_NONE;
 
 	/* completion status */
-	stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1);
-	stat1 &= MXS_DMA_CHANNELS_MASK;
-	writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
+	completed = readl(mxs_dma->base + HW_APBHX_CTRL1);
+	completed = (completed >> chan) & 0x1;
+
+	/* Clear interrupt */
+	writel((1 << chan),
+			mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
 
 	/* error status */
-	stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2);
-	writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
+	err = readl(mxs_dma->base + HW_APBHX_CTRL2);
+	err &= (1 << (MXS_DMA_CHANNELS + chan)) | (1 << chan);
+
+	/*
+	 * error status bit is in the upper 16 bits, error irq bit in the lower
+	 * 16 bits. We transform it into a simpler error code:
+	 * err: 0x00 = no error, 0x01 = TERMINATION, 0x02 = BUS_ERROR
+	 */
+	err = (err >> (MXS_DMA_CHANNELS + chan)) + (err >> chan);
+
+	/* Clear error irq */
+	writel((1 << chan),
+			mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
 
 	/*
 	 * When both completion and error of termination bits set at the
 	 * same time, we do not take it as an error.  IOW, it only becomes
-	 * an error we need to handle here in case of either it's (1) a bus
-	 * error or (2) a termination error with no completion.
+	 * an error we need to handle here in case of either it's a bus
+	 * error or a termination error with no completion. 0x01 is termination
+	 * error, so we can subtract err & completed to get the real error case.
 	 */
-	stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) | /* (1) */
-		(~(stat2 >> MXS_DMA_CHANNELS) & stat2 & ~stat1); /* (2) */
-
-	/* combine error and completion status for checking */
-	stat1 = (stat2 << MXS_DMA_CHANNELS) | stat1;
-	while (stat1) {
-		int channel = fls(stat1) - 1;
-		struct mxs_dma_chan *mxs_chan =
-			&mxs_dma->mxs_chans[channel % MXS_DMA_CHANNELS];
-
-		if (channel >= MXS_DMA_CHANNELS) {
-			dev_dbg(mxs_dma->dma_device.dev,
-				"%s: error in channel %d\n", __func__,
-				channel - MXS_DMA_CHANNELS);
-			mxs_chan->status = DMA_ERROR;
-			mxs_dma_reset_chan(mxs_chan);
-		} else {
-			if (mxs_chan->flags & MXS_DMA_SG_LOOP)
-				mxs_chan->status = DMA_IN_PROGRESS;
-			else
-				mxs_chan->status = DMA_SUCCESS;
-		}
+	err -= err & completed;
 
-		stat1 &= ~(1 << channel);
+	mxs_chan = &mxs_dma->mxs_chans[chan];
 
-		if (mxs_chan->status == DMA_SUCCESS)
-			dma_cookie_complete(&mxs_chan->desc);
-
-		/* schedule tasklet on this channel */
-		tasklet_schedule(&mxs_chan->tasklet);
+	if (err) {
+		dev_dbg(mxs_dma->dma_device.dev,
+			"%s: error in channel %d\n", __func__,
+			chan);
+		mxs_chan->status = DMA_ERROR;
+		mxs_dma_reset_chan(mxs_chan);
+	} else {
+		if (mxs_chan->flags & MXS_DMA_SG_LOOP)
+			mxs_chan->status = DMA_IN_PROGRESS;
+		else
+			mxs_chan->status = DMA_SUCCESS;
 	}
 
+	if (mxs_chan->status == DMA_SUCCESS)
+		dma_cookie_complete(&mxs_chan->desc);
+
+	/* schedule tasklet on this channel */
+	tasklet_schedule(&mxs_chan->tasklet);
+
 	return IRQ_HANDLED;
 }