patch-2.1.97 linux/drivers/char/serial.c

Next file: linux/drivers/char/tty_io.c
Previous file: linux/drivers/char/mem.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.96/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -25,50 +25,18 @@
  *  8/97: Fix bug in rs_set_termios with RTS
  *        Stanislav V. Voronyi <stas@uanet.kharkov.ua>
  *
+ *  3/98: Change the IRQ detection, use of probe_irq_o*(),
+ *	  supress TIOCSERGWILD and TIOCSERSWILD
+ *	  Etienne Lorrain <etienne.lorrain@ibm.net>
+ *
+ *  4/98: Added changes to support the ARM architecture proposed by
+ * 	  Russell King
+ *
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
  */
 
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/serial_reg.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <linux/init.h>
-#ifdef CONFIG_SERIAL_CONSOLE
-#include <linux/console.h>
-#endif
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-
-static char *serial_name = "Serial driver";
-static char *serial_version = "4.24";
-
-static DECLARE_TASK_QUEUE(tq_serial);
-
-static struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
 /*
  * Serial driver configuration section.  Here are the various options:
  *
@@ -86,6 +54,9 @@
  * CONFIG_SERIAL_SHARE_IRQ
  * 		Enables support for multiple serial ports on one IRQ
  *
+ * CONFIG_SERIAL_DETECT_IRQ
+ *		Enable the autodetection of IRQ on standart ports
+ *
  * SERIAL_PARANOIA_CHECK
  * 		Check the magic number for the async_structure where
  * 		ever possible.
@@ -100,6 +71,7 @@
 
 #define CONFIG_SERIAL_MANY_PORTS
 #define CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_DETECT_IRQ
 #define CONFIG_SERIAL_MULTIPORT
 #define CONFIG_HUB6
 #endif
@@ -133,7 +105,7 @@
 
 #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
 
-#define _INLINE_ inline
+#define SERIAL_INLINE
   
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
@@ -143,22 +115,70 @@
 #endif
 
 /*
+ * End of serial driver configuration section.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/serial.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+	
+static char *serial_name = "Serial driver";
+static char *serial_version = "4.25";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
  * IRQ_timeout		- How long the timeout should be for each IRQ
  * 				should be after the IRQ has been active.
  */
 
-static struct async_struct *IRQ_ports[16];
+static struct async_struct *IRQ_ports[NR_IRQS];
 #ifdef CONFIG_SERIAL_MULTIPORT
-static struct rs_multiport_struct rs_multiport[16];
+static struct rs_multiport_struct rs_multiport[NR_IRQS];
 #endif
-static int IRQ_timeout[16];
-static volatile int rs_irq_triggered;
-static volatile int rs_triggered;
-static int rs_wild_int_mask;
+static int IRQ_timeout[NR_IRQS];
 #ifdef CONFIG_SERIAL_CONSOLE
 static struct console sercons;
 #endif
 
+static unsigned detect_uart_irq (struct serial_state * state);
 static void autoconfig(struct serial_state * info);
 static void change_speed(struct async_struct *info);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
@@ -180,109 +200,9 @@
 	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
 	{ 0, 0}
 };
-	
-/*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
- */
-#define BASE_BAUD ( 1843200 / 16 )
-
-/* Standard COM flags (except for COM4, because of the 8514 problem) */
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
-#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
-
-
-#ifdef CONFIG_SERIAL_MANY_PORTS
-#define FOURPORT_FLAGS ASYNC_FOURPORT
-#define ACCENT_FLAGS 0
-#define BOCA_FLAGS 0
-#define HUB6_FLAGS 0
-#endif
-	
-/*
- * The following define the access methods for the HUB6 card. All
- * access is through two ports for all 24 possible chips. The card is
- * selected through the high 2 bits, the port on that card with the
- * "middle" 3 bits, and the register on that port with the bottom
- * 3 bits.
- *
- * While the access port and interrupt is configurable, the default
- * port locations are 0x302 for the port control register, and 0x303
- * for the data read/write register. Normally, the interrupt is at irq3
- * but can be anything from 3 to 7 inclusive. Note that using 3 will
- * require disabling com2.
- */
-
-#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
 
 static struct serial_state rs_table[] = {
-	/* UART CLK   PORT IRQ     FLAGS        */
-	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */
-	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */
-	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */
-	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
-#ifdef CONFIG_SERIAL_MANY_PORTS
-	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */
-	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */
-	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */
-	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */
-
-	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */
-	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */
-	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */
-	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */
-	
-	{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */
-	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */
-	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare; user configurable) */
-	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare; user configurable) */
-
-	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */
-	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */
-	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */
-	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */
-	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */
-	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */
-	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */
-	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */
-	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */
-	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */
-	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */
-	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */
-	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */
-	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */
-	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */
-	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
-
-/* You can have up to four HUB6's in the system, but I've only
- * included two cards here for a total of twelve ports.
- */
-#ifdef CONFIG_HUB6
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS38 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS39 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS40 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS41 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */
-#endif
-#endif /* CONFIG_SERIAL_MANY_PORTS */
-#ifdef CONFIG_MCA
-	{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS },
-	{ 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS },
-#endif
+	SERIAL_PORT_DFNS	/* Defined in serial.h */
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
@@ -304,7 +224,7 @@
  * buffer across all the serial ports, since it significantly saves
  * memory if large numbers of serial ports are open.
  */
-static unsigned char *tmp_buf = 0;
+static unsigned char *tmp_buf;
 static struct semaphore tmp_buf_sem = MUTEX;
 
 static inline int serial_paranoia_check(struct async_struct *info,
@@ -443,17 +363,6 @@
  */
 
 /*
- * This is the serial driver's interrupt routine while we are probing
- * for submarines.
- */
-static void rs_probe(int irq, void *dev_id, struct pt_regs * regs)
-{
-	rs_irq_triggered = irq;
-	rs_triggered |= 1 << irq;
-	return;
-}
-
-/*
  * This routine is used by the interrupt handler to schedule
  * processing in the software interrupt portion of the driver.
  */
@@ -923,7 +832,7 @@
 	unsigned int	i;
 
 	if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
-		for (i=1; i < 16; i++) {
+		for (i=1; i < NR_IRQS; i++) {
 			info = IRQ_ports[i];
 			if (!info)
 				continue;
@@ -975,38 +884,6 @@
  */
 
 /*
- * Grab all interrupts in preparation for doing an automatic irq
- * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
- * mask of irq's which were grabbed and should therefore be freed
- * using free_all_interrupts().
- */
-static int grab_all_interrupts(int dontgrab)
-{
-	int 			irq_lines = 0;
-	int			i, mask;
-	
-	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-		if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) {
-			irq_lines |= mask;
-		}
-	}
-	return irq_lines;
-}
-
-/*
- * Release all interrupts grabbed by grab_all_interrupts
- */
-static void free_all_interrupts(int irq_lines)
-{
-	int	i;
-	
-	for (i = 0; i < 16; i++) {
-		if (irq_lines & (1 << i))
-			free_irq(i, NULL);
-	}
-}
-
-/*
  * This routine figures out the correct timeout for a particular IRQ.
  * It uses the smallest timeout of all of the serial ports in a
  * particular interrupt chain.  Now only used for IRQ 0....
@@ -1541,44 +1418,61 @@
 
 	if (!tty || !info->xmit_buf || !tmp_buf)
 		return 0;
-	    
-	if (from_user)
-		down(&tmp_buf_sem);
+
 	save_flags(flags);
-	while (1) {
-		cli();		
-		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				   SERIAL_XMIT_SIZE - info->xmit_head));
-		if (c <= 0)
-			break;
+	if (from_user) {
+		down(&tmp_buf_sem);
+		while (1) {
+			c = MIN(count,
+				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				    SERIAL_XMIT_SIZE - info->xmit_head));
+			if (c <= 0)
+				break;
 
-		if (from_user) {
 			c -= copy_from_user(tmp_buf, buf, c);
 			if (!c) {
 				if (!ret)
 					ret = -EFAULT;
 				break;
 			}
+			cli();
 			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				       SERIAL_XMIT_SIZE - info->xmit_head));
 			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-		} else
+			info->xmit_head = ((info->xmit_head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			info->xmit_cnt += c;
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		while (1) {
+			cli();		
+			c = MIN(count,
+				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				    SERIAL_XMIT_SIZE - info->xmit_head));
+			if (c <= 0) {
+				restore_flags(flags);
+				break;
+			}
 			memcpy(info->xmit_buf + info->xmit_head, buf, c);
-		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt += c;
-		restore_flags(flags);
-		buf += c;
-		count -= c;
-		ret += c;
+			info->xmit_head = ((info->xmit_head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			info->xmit_cnt += c;
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
 	}
-	if (from_user)
-		up(&tmp_buf_sem);
 	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
 	    !(info->IER & UART_IER_THRI)) {
 		info->IER |= UART_IER_THRI;
 		serial_out(info, UART_IER, info->IER);
 	}
-	restore_flags(flags);
 	return ret;
 }
 
@@ -1759,10 +1653,9 @@
 		goto check_and_exit;
 	}
 
-	if (new_serial.irq == 2)
-		new_serial.irq = 9;
+	new_serial.irq = irq_cannonicalize(new_serial.irq);
 
-	if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
+	if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) ||
 	    (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
 		return -EINVAL;
 	}
@@ -1793,6 +1686,7 @@
 	state->type = new_serial.type;
 	state->close_delay = new_serial.close_delay * HZ/100;
 	state->closing_wait = new_serial.closing_wait * HZ/100;
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 	info->xmit_fifo_size = state->xmit_fifo_size =
 		new_serial.xmit_fifo_size;
 
@@ -1951,9 +1845,9 @@
 	
 	shutdown(info);
 
-	cli();
 	autoconfig(info->state);
-	sti();
+	if ((info->state->flags & ASYNC_AUTO_IRQ) && (info->state->port != 0))
+		info->state->irq = detect_uart_irq(info->state);
 
 	retval = startup(info);
 	if (retval)
@@ -1984,50 +1878,6 @@
 	restore_flags(flags);
 }
 
-/*
- * This routine returns a bitfield of "wild interrupts".  Basically,
- * any unclaimed interrupts which is flapping around.
- */
-static int check_wild_interrupts(int doprint)
-{
-	int	i, mask;
-	int	wild_interrupts = 0;
-	int	irq_lines;
-	unsigned long timeout;
-	unsigned long flags;
-	
-	/* Turn on interrupts (they may be off) */
-	save_flags(flags); sti();
-
-	irq_lines = grab_all_interrupts(0);
-	
-	/*
-	 * Delay for 0.1 seconds -- we use a busy loop since this may 
-	 * occur during the bootup sequence
-	 */
-	timeout = jiffies+HZ/10;
-	while (timeout >= jiffies)
-		;
-	
-	rs_triggered = 0;	/* Reset after letting things settle */
-
-	timeout = jiffies+HZ/10;
-	while (timeout >= jiffies)
-		;
-	
-	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-		if ((rs_triggered & (1 << i)) &&
-		    (irq_lines & (1 << i))) {
-			wild_interrupts |= mask;
-			if (doprint)
-				printk("Wild interrupt?  (IRQ %d)\n", i);
-		}
-	}
-	free_all_interrupts(irq_lines);
-	restore_flags(flags);
-	return wild_interrupts;
-}
-
 #ifdef CONFIG_SERIAL_MULTIPORT
 static int get_multiport_struct(struct async_struct * info,
 				struct serial_multiport_struct *retinfo)
@@ -2155,8 +2005,7 @@
 		return -ENODEV;
 
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
-	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
 	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
 		if (tty->flags & (1 << TTY_IO_ERROR))
 		    return -EIO;
@@ -2178,23 +2027,9 @@
 		case TIOCSERCONFIG:
 			return do_autoconfig(info);
 
-		case TIOCSERGWILD:
-			return put_user(rs_wild_int_mask,
-					(unsigned int *) arg);
 		case TIOCSERGETLSR: /* Get line status register */
 			return get_lsr_info(info, (unsigned int *) arg);
 
-		case TIOCSERSWILD:
-			if (!suser())
-				return -EPERM;
-			error = get_user(rs_wild_int_mask,
-					 (unsigned int *) arg);
-			if (error)
-				return error;
-			if (rs_wild_int_mask < 0)
-				rs_wild_int_mask = check_wild_interrupts(0);
-			return 0;
-
 		case TIOCSERGSTRUCT:
 			if (copy_to_user((struct async_struct *) arg,
 					 info, sizeof(struct async_struct)))
@@ -2216,7 +2051,7 @@
  		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
 		 * Caller should use TIOCGICOUNT to see which one it was
 		 */
-		 case TIOCMIWAIT:
+		case TIOCMIWAIT:
 			cli();
 			/* note the counters on entry */
 			cprev = info->state->icount;
@@ -2707,6 +2542,7 @@
 #endif
 	tty->driver_data = info;
 	info->tty = tty;
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 	if (!tmp_buf) {
 		page = get_free_page(GFP_KERNEL);
@@ -2778,7 +2614,7 @@
  * /proc fs routines....
  */
 
-static int inline line_info(char *buf, struct serial_state *state)
+static inline int line_info(char *buf, struct serial_state *state)
 {
 	struct async_struct *info = state->info, scr_info;
 	char	stat_buf[30], control, status;
@@ -2906,7 +2742,10 @@
 #endif
 #ifdef CONFIG_SERIAL_SHARE_IRQ
 	printk(" SHARE_IRQ");
+#endif
 #define SERIAL_OPT
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+	printk(" DETECT_IRQ");
 #endif
 #ifdef SERIAL_OPT
 	printk(" enabled\n");
@@ -2917,105 +2756,54 @@
 }
 
 /*
- * This routine is called by do_auto_irq(); it attempts to determine
- * which interrupt a serial port is configured to use.  It is not
- * fool-proof, but it works a large part of the time.
- */
-static int get_auto_irq(struct async_struct *info)
-{
-        
-	unsigned char save_MCR, save_IER;
-	unsigned long timeout;
-#ifdef CONFIG_SERIAL_MANY_PORTS
-	unsigned char save_ICP=0;
-	unsigned short ICP=0, port = info->port;
-#endif
+ * This routine detect the IRQ of a serial port by clearing OUT2 when
+ * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at
+ * each time, as long as no other device permanently request the IRQ.
+ * If no IRQ is detected, or multiple IRQ appear, this function returns 0.
+ * The variable "state" and the field "state->port" should not be null.
+ */
+static unsigned detect_uart_irq (struct serial_state * state)
+{
+	int irq;
+	unsigned long irqs;
+	unsigned char save_mcr;
+	struct async_struct scr_info; /* serial_{in,out} because HUB6 */
 
-	
-	/*
-	 * Enable interrupts and see who answers
-	 */
-	rs_irq_triggered = 0;
-	cli();
-	save_IER = serial_inp(info, UART_IER);
-	save_MCR = serial_inp(info, UART_MCR);
 #ifdef CONFIG_SERIAL_MANY_PORTS
-	if (info->flags & ASYNC_FOURPORT)  {
-		serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
-		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
-		ICP = (port & 0xFE0) | 0x01F;
+	unsigned char save_ICP=0; /* no warning */
+	unsigned short ICP=0;
+
+	if (state->flags & ASYNC_FOURPORT)  {
+		ICP = (state->port & 0xFE0) | 0x01F;
 		save_ICP = inb_p(ICP);
 		outb_p(0x80, ICP);
 		(void) inb_p(ICP);
-	} else 
-#endif
-        {
-		serial_outp(info, UART_MCR,
-			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
-		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
-	}
-	sti();
-	/*
-	 * Next, clear the interrupt registers.
-	 */
-	(void)serial_inp(info, UART_LSR);
-	(void)serial_inp(info, UART_RX);
-	(void)serial_inp(info, UART_IIR);
-	(void)serial_inp(info, UART_MSR);
-	
-	timeout = jiffies+ ((2*HZ)/100);
-	while (timeout >= jiffies) {
-		if (rs_irq_triggered)
-			break;
 	}
-	/*
-	 * Now check to see if we got any business, and clean up.
-	 */
-	cli();
-	serial_outp(info, UART_IER, save_IER);
-	serial_outp(info, UART_MCR, save_MCR);
-#ifdef CONFIG_SERIAL_MANY_PORTS
-	if (info->flags & ASYNC_FOURPORT)
-		outb_p(save_ICP, ICP);
 #endif
-	sti();
-	return(rs_irq_triggered);
-}
+	scr_info.magic = SERIAL_MAGIC;
+	scr_info.port = state->port;
+	scr_info.flags = state->flags;
+#ifdef CONFIG_HUB6
+	scr_info.hub6 = state->hub6;
+#endif
 
-/*
- * Calls get_auto_irq() multiple times, to make sure we don't get
- * faked out by random interrupts
- */
-static int do_auto_irq(struct async_struct * info)
-{
-	unsigned 		port = info->port;
-	int 			irq_lines = 0;
-	int			irq_try_1 = 0, irq_try_2 = 0;
-	int			retries;
-	unsigned long flags;
+	/* forget possible initially masked and pending IRQ */
+	probe_irq_off(probe_irq_on());
+	save_mcr = serial_inp(&scr_info, UART_MCR);
 
-	if (!port)
-		return 0;
+	serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+	irqs = probe_irq_on();
+	serial_outp(&scr_info, UART_MCR, 0);
+	udelay (1);
+	irq = probe_irq_off(irqs);
 
-	/* Turn on interrupts (they may be off) */
-	save_flags(flags); sti();
+	serial_outp(&scr_info, UART_MCR, save_mcr);
 
-	irq_lines = grab_all_interrupts(rs_wild_int_mask);
-	
-	for (retries = 0; retries < 5; retries++) {
-		if (!irq_try_1)
-			irq_try_1 = get_auto_irq(info);
-		if (!irq_try_2)
-			irq_try_2 = get_auto_irq(info);
-		if (irq_try_1 && irq_try_2) {
-			if (irq_try_1 == irq_try_2)
-				break;
-			irq_try_1 = irq_try_2 = 0;
-		}
-	}
-	restore_flags(flags);
-	free_all_interrupts(irq_lines);
-	return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+	if (state->flags & ASYNC_FOURPORT)
+		outb_p(save_ICP, ICP);
+#endif
+	return (irq > 0)? irq : 0;
 }
 
 /*
@@ -3041,6 +2829,9 @@
 	info->magic = SERIAL_MAGIC;
 	info->port = state->port;
 	info->flags = state->flags;
+#ifdef CONFIG_HUB6
+	info->hub6 = state->hub6;
+#endif
 
 	save_flags(flags); cli();
 	
@@ -3086,13 +2877,6 @@
 		}
 	} 
 	
-	/*
-	 * If the AUTO_IRQ flag is set, try to do the automatic IRQ
-	 * detection.
-	 */
-	if (state->flags & ASYNC_AUTO_IRQ)
-		state->irq = do_auto_irq(info);
-		
 	scratch2 = serial_in(info, UART_LCR);
 	serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
 	serial_outp(info, UART_EFR, 0);	/* EFR is the same as FCR */
@@ -3174,6 +2958,7 @@
 	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
 				     UART_FCR_CLEAR_XMIT));
 	(void)serial_in(info, UART_RX);
+	serial_outp(info, UART_IER, 0);
 	
 	restore_flags(flags);
 }
@@ -3191,15 +2976,21 @@
 {
 	int i;
 	struct serial_state * state;
-	
+	extern void atomwide_serial_init (void);
+	extern void dualsp_serial_init (void);
+
+#ifdef CONFIG_ATOMWIDE_SERIAL
+	atomwide_serial_init ();
+#endif
+#ifdef CONFIG_DUALSP_SERIAL
+	dualsp_serial_init ();
+#endif
+
 	init_bh(SERIAL_BH, do_serial_bh);
 	timer_table[RS_TIMER].fn = rs_timer;
 	timer_table[RS_TIMER].expires = 0;
-#ifdef CONFIG_AUTO_IRQ
-	rs_wild_int_mask = check_wild_interrupts(1);
-#endif
 
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < NR_IRQS; i++) {
 		IRQ_ports[i] = 0;
 		IRQ_timeout[i] = 0;
 #ifdef CONFIG_SERIAL_MULTIPORT
@@ -3291,17 +3082,26 @@
 		state->icount.rx = state->icount.tx = 0;
 		state->icount.frame = state->icount.parity = 0;
 		state->icount.overrun = state->icount.brk = 0;
-		if (state->irq == 2)
-			state->irq = 9;
-		if (state->type == PORT_UNKNOWN) {
-			if (!(state->flags & ASYNC_BOOT_AUTOCONF))
-				continue;
-			if (check_region(state->port,8))
-				continue;
-			autoconfig(state);
-			if (state->type == PORT_UNKNOWN)
-				continue;
+		state->irq = irq_cannonicalize(state->irq);
+		if (check_region(state->port,8)) {
+			state->type = PORT_UNKNOWN;
+			continue;
 		}
+		if (   (state->type == PORT_UNKNOWN)
+		    && (state->flags & ASYNC_BOOT_AUTOCONF))
+			autoconfig(state);
+	}
+	/*
+	 * Detect the IRQ only once every port is initialised,
+	 * because some 16450 do not reset to 0 the MCR register.
+	 */
+	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+		if (state->type == PORT_UNKNOWN)
+			continue;
+		if (   (state->flags & ASYNC_BOOT_AUTOCONF)
+		    && (state->flags & ASYNC_AUTO_IRQ)
+		    && (state->port != 0))
+			state->irq = detect_uart_irq(state);
 		printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n",
 		       state->line,
 		       (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
@@ -3354,10 +3154,14 @@
 		printk("register_serial(): autoconfig failed\n");
 		return -1;
 	}
+	restore_flags(flags);
+
+	if ((state->flags & ASYNC_AUTO_IRQ) && (state->port != 0))
+		state->irq = detect_uart_irq(state);
+
 	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n",
 	       state->line, state->port, state->irq,
 	       uart_config[state->type].name);
-	restore_flags(flags);
 	return state->line;
 }
 
@@ -3597,7 +3401,7 @@
 	 *	Divisor, bytesize and parity
 	 */
 	ser = rs_table + co->index;
-	quot = BASE_BAUD / baud;
+	quot = ser->baud_base / baud;
 	cval = cflag & (CSIZE | CSTOPB);
 #if defined(__powerpc__) || defined(__alpha__)
 	cval >>= 8;
@@ -3613,13 +3417,12 @@
 	 *	Disable UART interrupts, set DTR and RTS high
 	 *	and set speed.
 	 */
-	outb(cval, ser->port + UART_LCR);  /* don't assume that DLAB is clear */
-	outb(0, ser->port + UART_IER);
-	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
 	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
 	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
 	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
 	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
+	outb(0, ser->port + UART_IER);
+	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
 
 	/*
 	 *	If we read 0xff from the LSR, there is no UART here.

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov