diff -Naur linux-2.2.14/Documentation/Configure.help linux-2.2.14-NecDuart/Documentation/Configure.help --- linux-2.2.14/Documentation/Configure.help Tue Apr 25 16:21:15 2000 +++ linux-2.2.14-NecDuart/Documentation/Configure.help Sun Dec 10 13:39:52 2000 @@ -1343,6 +1343,20 @@ Most people can say N here. +Bugfix for NEC NS16C55x UARTs +CONFIG_NEC_DUART_SUPPORT + Say Y here if you have a NEC NS1655x-family UART (NS16550Ax, + NS16C552x), and are experiencing problems (characters loss). + The standard serial driver is used to confuse the NEC UARTs + with Startech UARTs without FIFO support (NEC UARTs have + full 16550A FIFO). Use the "setserial" command to know if the + kernel has detected the wrong chip model. + + You must say Y also if you are using the SER2 or IOSPC64 cards + produced by CNi (www.cnisrl.it). + + If unsure, say N. + Extended dumb serial driver options CONFIG_SERIAL_EXTENDED If you wish to use any non-standard features of the standard "dumb" diff -Naur linux-2.2.14/drivers/char/Config.in linux-2.2.14-NecDuart/drivers/char/Config.in --- linux-2.2.14/drivers/char/Config.in Tue Apr 25 16:21:14 2000 +++ linux-2.2.14-NecDuart/drivers/char/Config.in Sun Dec 10 13:35:29 2000 @@ -10,6 +10,7 @@ fi tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL if [ "$CONFIG_SERIAL" = "y" ]; then + bool ' Bugfix for NEC UARTs (NS16C55x)' CONFIG_NEC_DUART_SUPPORT bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE fi bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED diff -Naur linux-2.2.14/drivers/char/serial.c linux-2.2.14-NecDuart/drivers/char/serial.c --- linux-2.2.14/drivers/char/serial.c Tue Apr 25 16:21:13 2000 +++ linux-2.2.14-NecDuart/drivers/char/serial.c Sun Dec 10 13:43:17 2000 @@ -154,8 +154,13 @@ #define _INLINE_ inline #endif + static char *serial_name = "Serial driver"; -static char *serial_version = "4.27"; +static char *serial_version = "4.27" +#ifdef CONFIG_NEC_DUART_SUPPORT + " - NECDUART fix 1.0" +#endif + ; static DECLARE_TASK_QUEUE(tq_serial); @@ -199,6 +204,18 @@ { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, +#ifdef CONFIG_NEC_DUART_SUPPORT + /* The NEC NS16C55x is actually a 16550A. If I added + * a specific record for this particular chipset in "uart_config", + * I would have to change both "serial.h" and the "setserial" + * utility. I don't think it's worth. Anyway, this source is + * ready to accept the change. */ +#ifndef PORT_NS16C550AF +#define PORT_NS16C550AF (PORT_MAX + 1) +#else + { "NS16C55x", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, +#endif +#endif { 0, 0} }; @@ -2821,6 +2838,10 @@ static _INLINE_ void show_serial_version(void) { printk(KERN_INFO "%s version %s with", serial_name, serial_version); +#ifdef CONFIG_NEC_DUART_SUPPORT + printk(" NEC_DUART"); +#define SERIAL_OPT +#endif #ifdef CONFIG_HUB6 printk(" HUB-6"); #define SERIAL_OPT @@ -2959,8 +2980,8 @@ scratch2 = serial_inp(info, UART_IER); serial_outp(info, UART_IER, scratch); if (scratch2) { - restore_flags(flags); - return; /* We failed; there's nothing here */ + /* We failed; there's nothing here */ + goto autoconfig_END_fail; } /* @@ -2979,8 +3000,7 @@ status1 = serial_inp(info, UART_MSR) & 0xF0; serial_outp(info, UART_MCR, scratch); if (status1 != 0x90) { - restore_flags(flags); - return; + goto autoconfig_END_fail; } } @@ -3008,13 +3028,61 @@ /* Check for Startech UART's */ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); if (serial_in(info, UART_EFR) == 0) { +#ifdef CONFIG_NEC_DUART_SUPPORT + /* giusguerrini@racine.ra.it: Both ST16650 and NS16C55x + * behaves the same, so the ortiginal algorithm is not + * enough to detect them. We have to do something + * smarter. */ + /* OK, there could be an ST16650 or a NEC NS16C55x. + * The former uses many bits in EFR(=FCR+DLAB), the latter + * uses only bits 0, 1 and 2, and zeroes other bits. + * So, we'll try to detect an ST16650 by setting some + * high bit in EFR (e.g. the CTS protocol enabler), and + * then verifying if it has really been set. */ + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_CTS); + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) & UART_EFR_CTS) { + /* bit set succeeded. It's an ST16650. */ + state->type = PORT_16650; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + } + else { + /* bit set unsuccesful. Perhaps we are using a NEC double UART. + * We must remeber it, because + * - this chip fully supports the 166550A mode, + * - this chip doesn't like the TI 16750 + * detection algorithm, so we must avoid it. */ + state->type = PORT_NS16C550AF; + serial_outp(info, UART_LCR, scratch2); + } +#else /* CONFIG_NEC_DUART_SUPPORT */ state->type = PORT_16650; +#endif /* CONFIG_NEC_DUART_SUPPORT */ } else { serial_outp(info, UART_LCR, 0xBF); if (serial_in(info, UART_EFR) == 0) state->type = PORT_16650V2; } } +#ifdef CONFIG_NEC_DUART_SUPPORT + if (state->type == PORT_NS16C550AF) { +#if PORT_NS16C550AF == (PORT_MAX + 1) + /* Hide the "internal" type code. I'm too lazy to change + * serail.h, setserial,... But the code is ready. */ + state->type = PORT_16550A; +#endif + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } + else + /* giusguerrini@racine.ra.it: Arrrgh! This messes up the NEC + * NS16C552A dual UART. Bit 0 of FCR+DLAB enables concurrent write + * on registers of BOTH UARTs. So, with this chip we can't write + * the ENABLE_FIFO bit when DLAB is set. That's why we won't + * execute this code if we detected a NS16C55x (and, anyway, + * we have already detected the chip!). */ +#endif /* CONFIG_NEC_DUART_SUPPORT */ if (state->type == PORT_16550A) { /* Check for TI 16750 */ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); @@ -3044,8 +3112,7 @@ state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; if (state->type == PORT_UNKNOWN) { - restore_flags(flags); - return; + goto autoconfig_END_fail; } request_region(info->port,8,"serial(auto)"); @@ -3066,8 +3133,13 @@ UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); serial_outp(info, UART_IER, 0); - + +autoconfig_END: restore_flags(flags); + return; + +autoconfig_END_fail: + goto autoconfig_END; } int register_serial(struct serial_struct *req);