Subject: Re: Question about ppc4xx_dma.h

From: akuster <[email-masked]>
Date: Mon, 26 Aug 2002 20:44:18 -0700

Todd Poynor wrote:
>
> Noticed that the order of clearing the old transfer mode bits and
> setting the p_dma_ch->mode bits is reversed in the new patch, not sure
> if this causes problems:
>
> +       tmp_cntl |= (p_dma_ch->mode | DMA_CH_ENABLE);
> +
> +       switch (dmanr) {
> +       case 0:
> +               control = mfdcr(DCRN_DMACR0);
> +               control |= tmp_cntl;
> +               control &= ~(DMA_TM_MASK | DMA_TD);     /* clear all
> mode bits */
>
> This seems to set and then clear the p_dma_ch->mode bits in control
> prior to writing to the DMACR, a problem?
>
>
> --
> Todd
>
>
>
>
>

Here is a patch that should address the above issue.  I put in
additional checks for if a the current dma channel is all ready claimed
and if the requestion channel is greater than max dma channels.
dma_enable no longer clears mode bits.   ( TODO: should document 4xx dma
usage)

Armin
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux 2.4 for PowerPC development tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1140  -> 1.1141
#	arch/ppc/kernel/ppc4xx_dma.c	1.14    -> 1.15
#	include/asm-ppc/ppc4xx_dma.h	1.15    -> 1.16
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/08/26	[email-masked]	1.1141
# fixed enable to allow more than 1 channel support pointed out by Jim Duey
# added check in enable / disable and for chan > MAX
# added in_use field in struct to make sure you can enable a chan twice
# and new return error code
# --------------------------------------------
#
diff -Nru a/arch/ppc/kernel/ppc4xx_dma.c b/arch/ppc/kernel/ppc4xx_dma.c
--- a/arch/ppc/kernel/ppc4xx_dma.c	Mon Aug 26 20:37:25 2002
+++ b/arch/ppc/kernel/ppc4xx_dma.c	Mon Aug 26 20:37:25 2002
@@ -34,6 +34,11 @@
  *
  *  Version 1.2 07/23/02 - Armin
  *  added CE to stuct in get_config
+ *
+ *  Version 1.3 08/21/02 - Armin
+ *  fixed enable to allow more than 1 channel support pointed out by
+ *  Jim Duey
+ *
  *
  */

@@ -136,8 +141,12 @@
 enable_dma(unsigned int dmanr)
 {
 	unsigned int control;
+	unsigned int tmp_cntl = 0;
 	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

+	if (p_dma_ch->in_use)
+		return DMA_STATUS_CHANNEL_NOTFREE;
+
 	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
 		printk("enable_dma: bad channel: %d\n", dmanr);
 		return DMA_STATUS_BAD_CHANNEL;
@@ -153,15 +162,39 @@
 		set_dst_addr(dmanr, 0);
 	}
 	/* for other xfer modes, the addresses are already set */
-	control = mfdcr(DCRN_DMACR0);
-	control &= ~(DMA_TM_MASK | DMA_TD);	/* clear all mode bits */
-	if (p_dma_ch->mode == DMA_MODE_MM) {
+		if (p_dma_ch->mode == DMA_MODE_MM) {
 		/* software initiated memory to memory */
-		control |= control | DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
+		tmp_cntl |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
 	}
-	control |= (p_dma_ch->mode | DMA_CH_ENABLE);
-	mtdcr(DCRN_DMACR0, control);
+	tmp_cntl |= (p_dma_ch->mode | DMA_CH_ENABLE);

+	switch (dmanr) {
+	case 0:
+		control = mfdcr(DCRN_DMACR0);
+		control |= tmp_cntl;
+	       	mtdcr(DCRN_DMACR0, control);
+		break;
+	case 1:
+		control = mfdcr(DCRN_DMACR1);
+		control |= tmp_cntl;
+		mtdcr(DCRN_DMACR1, control);
+		break;
+	case 2:
+		control = mfdcr(DCRN_DMACR2);
+		control |= tmp_cntl;
+		mtdcr(DCRN_DMACR2, control);
+		break;
+	case 3:
+		control = mfdcr(DCRN_DMACR3);
+		control |= tmp_cntl;
+		mtdcr(DCRN_DMACR3, control);
+		break;
+	default:
+#ifdef DEBUG_4xxDMA
+		printk("enable_dma: bad channel: %d\n", dmanr);
+#endif
+	}
+	p_dma_ch->in_use = 1;
 	return DMA_STATUS_GOOD;
 }

@@ -169,6 +202,15 @@
 disable_dma(unsigned int dmanr)
 {
 	unsigned int control;
+	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+	if (!p_dma_ch->in_use)
+		return DMA_STATUS_CHANNEL_NOTFREE;
+
+	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+		printk("enable_dma: bad channel: %d\n", dmanr);
+		return DMA_STATUS_BAD_CHANNEL;
+	}

 	switch (dmanr) {
 	case 0:
@@ -196,6 +238,7 @@
 		printk("disable_dma: bad channel: %d\n", dmanr);
 #endif
 	}
+	p_dma_ch->in_use = 0;
 }

 /*
@@ -348,6 +391,12 @@
 set_dma_addr(unsigned int dmanr, phys_addr_t addr)
 {
 	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+		printk("enable_dma: bad channel: %d\n", dmanr);
+		return DMA_STATUS_BAD_CHANNEL;
+	}
+
 #ifdef DEBUG_4xxDMA
 	{
 		int error = 0;
@@ -495,7 +544,7 @@
 	unsigned int control;
 	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

-	p_dma_ch->int_enable = TRUE;
+	p_dma_ch->int_enable = FALSE;
 	switch (dmanr) {
 	case 0:
 		control = mfdcr(DCRN_DMACR0);
@@ -545,11 +594,11 @@
 	DMA_MODE_READ = (unsigned long) DMA_TD;	/* Peripheral to Memory */
 	DMA_MODE_WRITE = 0;	/* Memory to Peripheral */

-#ifdef DEBUG_4xxDMA
 	if (!p_init) {
 		printk("hw_init_dma_channel: NULL p_init\n");
 		return DMA_STATUS_NULL_POINTER;
 	}
+#ifdef DEBUG_4xxDMA
 	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
 		printk("hw_init_dma_channel: bad channel %d\n", dmanr);
 		return DMA_STATUS_BAD_CHANNEL;
diff -Nru a/include/asm-ppc/ppc4xx_dma.h b/include/asm-ppc/ppc4xx_dma.h
--- a/include/asm-ppc/ppc4xx_dma.h	Mon Aug 26 20:37:25 2002
+++ b/include/asm-ppc/ppc4xx_dma.h	Mon Aug 26 20:37:25 2002
@@ -26,6 +26,10 @@
  *    moved scatter/gather inline code to ppc4xx_sgdma.c
  *    added three new extern proto types for the STBxxxx API's
  *
+ *    version 1.4: 08/26/02 - Armin
+ *    added in_use field for when a channel is claimed
+ *    added new return code *_NOTFREE
+ *
  */

 #ifdef __KERNEL__
@@ -57,6 +61,7 @@
 #define DMA_STATUS_OUT_OF_MEMORY	5
 #define DMA_STATUS_SGL_LIST_EMPTY	6
 #define DMA_STATUS_GENERAL_ERROR	7
+#define DMA_STATUS_CHANNEL_NOTFREE	8

 #define DMA_CHANNEL_BUSY		0x80000000

@@ -411,7 +416,9 @@
 #endif

 typedef struct {
-
+	bool in_use;	/* set when channel is being used, clr when
+			   avaliable
+			 */
 	/*
 	 * Valid polarity settings:
 	 *   DMAReq0_ActiveLow