[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Bug in linux-2_4_26.diff in combination with a Deboard LX?



Dear all,

I believe I have found a bug in the linux-2_4_26.diff file, dated June
3rd, 2004, as it can currently be found on the developer.axis.com website
in the download/linux section and distributed as part of the
download/devboard_lx/R2_2_0/devboard_lx-R2_2_0.tar.gz SDK release 2.2.0
(June 3rd, 2004 edition) for the devboard LX.  I applied the fix to the
'flashit' script (jffs => jffs2). 

Using stock Linux 2.4.26 with the patch, as supplied by Axis, the
resulting kernel will not boot using the 'ktest' script provided in the
SDK.  At least no output appears on serial port 2 (or any other port).

After some testing I found that the kernel will boot when a slight change
is applied.  I replaced the file arch/cris/kernel/debugport.c with the
same file as provided by the stock kernel and removed support for the
rs_debug_write_function() in arch/cris/drivers/serial.c.  The attached
patch will do this (apply this patch to the kernel after applying the Axis
patch).

I would like to know whether others can verify this, specifically the
poster of message http://mhonarc.axis.se/dev-etrax/msg04459.html (Eric
Sorton), as his post seems to indicate the same problem.

My solution is probably too coarse, but I did not study the code
extensively to find a more elegant solution.

For reference, I don't know if it matters, my board is an Axis Devboard
LX, and the numbers provided on the board say: PCB AXIS DEVBOARD E5,
19784, R2, and the Etrax 100LX chip is a V2 variant, as indicated by the
number 18816 on the chip.

Kind regards,

-- 
Ferdy Hanssen, DIstributed and Embedded Systems group;
University of Twente, Dept. of Computer Science;
e-mail: hanssen@xxxxxxx.nl; tel: +31 53 489 3744;
fax: +31 53 489 4590; PO Box 217, 7500AE Enschede, NL
diff -urN linux-2.4.26.axis/arch/cris/drivers/serial.c linux-2.4.26/arch/cris/drivers/serial.c
--- linux-2.4.26.axis/arch/cris/drivers/serial.c	2004-07-02 22:58:42.000000000 +0200
+++ linux-2.4.26/arch/cris/drivers/serial.c	2004-07-02 22:47:45.000000000 +0200
@@ -4502,6 +4502,7 @@
 	
 }
 
+#if 0
 /* In debugport.c - register a console write function that uses the normal
  * serial driver 
  */
@@ -4537,6 +4538,7 @@
         }
         return 0;
 }
+#endif
 
 /*
  * ------------------------------------------------------------
@@ -5336,7 +5338,9 @@
 	}
 #endif
 #endif /* CONFIG_SVINTO_SIM */
+#if 0
 	debug_write_function = rs_debug_write_function;
+#endif
 	return 0;
 }
 
diff -urN linux-2.4.26.axis/arch/cris/kernel/debugport.c linux-2.4.26/arch/cris/kernel/debugport.c
--- linux-2.4.26.axis/arch/cris/kernel/debugport.c	2004-07-02 22:58:42.000000000 +0200
+++ linux-2.4.26/arch/cris/kernel/debugport.c	2003-08-25 13:44:39.000000000 +0200
@@ -12,17 +12,6 @@
  *    init_etrax_debug()
  *
  * $Log: debugport.c,v $
- * Revision 1.12  2004/01/30 07:23:32  starvik
- * Disable IRQs in console_write
- *
- * Revision 1.11  2004/01/27 12:43:29  starvik
- * Disable serial port DMAs
- *
- * Revision 1.10  2004/01/27 11:45:38  starvik
- * Removed all the ugly DMA hacks to make it work when the serial port driver
- * is interrupt driven. Now uses the real serial port driver if available
- * (fallbacks to manual mode).
- *
  * Revision 1.9  2003/02/17 07:10:34  starvik
  * Last merge was incomplete
  *
@@ -58,7 +47,6 @@
 #include <asm/system.h>
 #include <asm/svinto.h>
 #include <asm/io.h>             /* Get SIMCOUT. */
-#include <asm/hardirq.h>
 
 /* Which serial-port is our debug port ? */
 
@@ -118,29 +106,23 @@
 #define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr)
 #endif
 
-/* Used by serial.c to register a debug_write_function so that the normal
- * serial driver is used for kernel debug output
- */
-typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
+#define MIN_SIZE 32 /* Size that triggers the FIFO to flush characters to interface */
 
-debugport_write_function debug_write_function = NULL;
-
-static void 
-console_write_direct(struct console *co, const char *buf, unsigned int len)
-{  
-	int i;
-	/* Send data */
-	for (i = 0; i < len; i++) {
-		/* Wait until transmitter is ready and send.*/
-		while(!(*DEBUG_READ & IO_MASK(R_SERIAL0_READ, tr_ready)));
-                *DEBUG_WRITE = buf[i];
-	}
-}
+/* Write a string of count length to the console (debug port) using DMA, polled
+ * for completion. Interrupts are disabled during the whole process. Some
+ * caution needs to be taken to not interfere with ttyS business on this port.
+ */
 
 static void 
 console_write(struct console *co, const char *buf, unsigned int len)
 {
-	unsigned long flags;
+	static struct etrax_dma_descr descr;
+	static struct etrax_dma_descr descr2;
+	static char tmp_buf[MIN_SIZE];
+	static int tmp_size = 0;
+
+	unsigned long flags; 
+	
 #ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
         /* no debug printout at all */
         return;
@@ -151,19 +133,86 @@
 	SIMCOUT(buf,len);
 	return;
 #endif
+	
+	save_flags(flags);
+	cli();
 
 #ifdef CONFIG_ETRAX_KGDB
 	/* kgdb needs to output debug info using the gdb protocol */
 	putDebugString(buf, len);
+	restore_flags(flags);
 	return;
 #endif
-
-	local_irq_save(flags);
-	if (debug_write_function)
-		if (debug_write_function(co->index, buf, len))
+	/* To make this work together with the real serial port driver
+	 * we have to make sure that everything is flushed when we leave
+	 * here. The following steps are made to assure this:
+	 * 1. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
+	 * 2. Write at least half the FIFO to trigger flush to serial port.
+	 * 3. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
+         */
+
+	/* Do we have enough characters to make the DMA/FIFO happy? */
+	if (tmp_size + len < MIN_SIZE)
+	{
+		int size = min((int)(MIN_SIZE - tmp_size),(int)len);
+		memcpy(&tmp_buf[tmp_size], buf, size);
+		tmp_size += size;
+		len -= size;
+        
+		/* Pad with space if complete line */
+		if (tmp_buf[tmp_size-1] == '\n')
+		{
+			memset(&tmp_buf[tmp_size-1], ' ', MIN_SIZE - tmp_size);
+			tmp_buf[MIN_SIZE - 1] = '\n';
+			tmp_size = MIN_SIZE;
+			len = 0;
+		}
+		else
+		{
+                  /* Wait for more characters */
+			restore_flags(flags);
 			return;
-	console_write_direct(co, buf, len);
-	local_irq_restore(flags);
+		}
+	}
+
+	/* make sure the transmitter is enabled. 
+	 * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS.
+	 * in the future, move the tr/rec_ctrl shadows from etrax100ser.c to
+	 * shadows.c and use it here as well...
+	 */
+
+	*DEBUG_TR_CTRL = 0x40;
+	while(*DEBUG_OCMD & 7); /* Until DMA is not running */
+	while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
+	udelay(200); /* Wait for last two characters to leave the serial transmitter */
+
+	if (tmp_size)
+	{
+		descr.ctrl = len ?  0 : d_eop | d_wait | d_eol;
+		descr.sw_len = tmp_size;
+		descr.buf = virt_to_phys(tmp_buf);
+		descr.next = virt_to_phys(&descr2);
+		descr2.ctrl = d_eop | d_wait | d_eol;
+		descr2.sw_len = len;
+		descr2.buf = virt_to_phys((char*)buf);
+	}
+	else
+	{
+		descr.ctrl = d_eop | d_wait | d_eol;
+		descr.sw_len = len;
+		descr.buf = virt_to_phys((char*)buf);
+	}
+
+	*DEBUG_FIRST = virt_to_phys(&descr); /* write to R_DMAx_FIRST */
+	*DEBUG_OCMD = 1;       /* dma command start -> R_DMAx_CMD */
+
+	/* wait until the output dma channel is ready again */
+	while(*DEBUG_OCMD & 7);
+	while(*DEBUG_STATUS & 0x7f);
+	udelay(200);
+
+	tmp_size = 0;
+	restore_flags(flags);
 }
 
 /* legacy function */
@@ -245,22 +294,4 @@
 init_etrax_debug(void)
 {
 	register_console(&sercons);
-#if CONFIG_ETRAX_DEBUG_PORT_NULL
-	return;
-#endif
-
-#if DEBUG_PORT_IDX == 0
-	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
-	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
-#elif DEBUG_PORT_IDX == 1
-	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
-	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
-#elif DEBUG_PORT_IDX == 2
-	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
-	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
-#elif DEBUG_PORT_IDX == 3
-	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
-	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);     
-#endif
-	*R_GEN_CONFIG = genconfig_shadow;
 }