High speed serial ports

Pavel Machek (pavel@bug.ucw.cz)
Mon, 7 Dec 1998 19:59:27 +0100


Hi!

This patch introduces high speed serial ports, relative to 2.1.131. It
works for me and is hopefully cleaner than previous version. Would
something like this be considered for inclusion to 2.3?

Pavel

PS: Testers wanted!

--- clean//drivers/char/serial.c Tue Dec 1 22:11:36 1998
+++ linux/drivers/char/serial.c Sun Dec 6 22:19:01 1998
@@ -3003,6 +3003,10 @@
if ((status1 != 0xa5) || (status2 != 0x5a))
state->type = PORT_8250;
}
+#ifdef CONFIG_SERIAL_HISPEED
+ if (state->type == PORT_16550A)
+ enable_hispeed(state);
+#endif
state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size;

if (state->type == PORT_UNKNOWN) {
@@ -3280,6 +3284,10 @@
free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
}
+#ifdef CONFIG_HISPEED
+ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++)
+ disable_hispeed(state);
+#endif
}
#endif /* MODULE */

--- clean//drivers/char/Config.in Sat Oct 24 22:24:25 1998
+++ linux/drivers/char/Config.in Fri Dec 4 00:17:26 1998
@@ -7,6 +7,7 @@
bool 'Virtual terminal' CONFIG_VT
if [ "$CONFIG_VT" = "y" ]; then
bool 'Support for console on virtual terminal' CONFIG_VT_CONSOLE
+ bool 'Keylink support' CONFIG_KEYLINK
fi
tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL
if [ "$CONFIG_SERIAL" = "y" ]; then
@@ -19,6 +20,7 @@
bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ
bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT
bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6
+ bool ' Support for >115200 with modern chipsets (dangerous)' CONFIG_SERIAL_HISPEED
fi
bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
@@ -135,5 +137,9 @@
source drivers/char/ftape/Config.in
fi
endmenu
+
+if [ "$CONFIG_BUSMOUSE" != "n" -o "$CONFIG_UMISC" != "n" -o "$CONFIG_PSMOUSE" != "n" -o "$CONFIG_MS_BUSMOUSE" != "n" -o "$CONFIG_ATIXL_BUSMOUSE" != "n" -o "$CONFIG_SOFT_WATCHDOG" != "n" -o "$CONFIG_PCWATCHDOG" != "n" -o "$CONFIG_APM" != "n" -o "$CONFIG_RTC" != "n" -o "$CONFIG_SUN_MOUSE" != "n" -o "$CONFIG_NVRAM" != "n" -o "$CONFIG_KEYLINK" != "n" ]; then
+ define_bool CONFIG_MISC y
+fi

endmenu
--- clean//drivers/char/Makefile Thu Nov 26 00:20:23 1998
+++ linux/drivers/char/Makefile Fri Dec 4 15:32:08 1998
@@ -31,18 +31,28 @@
ifeq ($(CONFIG_SERIAL),y)
ifeq ($(CONFIG_SUN_SERIAL),)
LX_OBJS += serial.o
+ ifeq ($(CONFIG_SERIAL_HISPEED),y)
+ L_OBJS += hispeed.o
+ endif
endif
else
ifeq ($(CONFIG_SERIAL),m)
ifeq ($(CONFIG_SUN_SERIAL),)
MX_OBJS += serial.o
+ ifeq ($(CONFIG_SERIAL_HISPEED),y)
+ MX_OBJS += hispeed.o
+ endif
endif
endif
endif

ifndef CONFIG_SUN_KEYBOARD
ifdef CONFIG_VT
-L_OBJS += keyboard.o
+ifdef CONFIG_KEYLINK
+L_OBJS += keylink.o
+M = y
+endif
+LX_OBJS += keyboard.o
endif
ifneq ($(ARCH),m68k)
L_OBJS += pc_keyb.o defkeymap.o
--- /dev/null Wed May 21 13:24:05 1997
+++ linux/drivers/char/hispeed.c Sun Dec 6 22:25:49 1998
@@ -0,0 +1,662 @@
+/*
+ * linux/drivers/char/serial.c
+ *
+ * Copyright (C) 1998 Pavel Machek, it is cleanup of code from:
+ *
+ * High Speed mode patch for SMC hispeed_chips by mizuhara@st.rim.or.jp
+ * Original idea and NT driver from gigo@yk.rim.or.jp
+ * 09/98 updated by ytakeuch@po.iijnet.or.jp
+ * Visit http://www.yk.rim.or.jp/~gigo/download.html
+ */
+
+/*
+ * CONFIG_SERIAL_HISPEED
+ * Enables support for High Speed mode, available with
+ * some SMC/NS/Winbond multi-I/O hispeed_chips.
+ */
+
+#define DEBUG 0
+
+#define PC87338
+#undef CHKENB
+
+#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>
+
+struct idn {
+ unsigned short port; /* Base port chip lives at */
+ unsigned char ini1; /* 1st magic for entering config mode */
+ unsigned char ini2; /* 2nd... */
+ unsigned char idx; /* Magic for getting identification */
+ unsigned char id; /* ... identification expected */
+ unsigned char rev;
+ unsigned char fin; /* Magic for exiting config mode */
+ int pch; /* Manufacturer, for printing only */
+ int type;
+ char *name;
+} idnt[] = {
+ { 0x3f0,0x55,0x55,0x20,0x4c,0x01,0xAA,1,4,"B72x" }, /*00:B72x */
+ { 0x370,0x55,0x55,0x20,0x4c,0x01,0xAA,1,4,"B72x" }, /*01:B72x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"B77x" }, /*02:B77x */
+ { 0x370,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"B77x" }, /*03:B77x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x44,0x01,0xAA,1,4,"B78x" }, /*04:B78x */
+ { 0x370,0x55,0x55,0x20,0x44,0x01,0xAA,1,4,"B78x" }, /*05:B78x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"B80x" }, /*06:B80x */
+ { 0x370,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"B80x" }, /*07:B80x at 0x370 */
+ { 0x3f0,0x55,0x55,0x0d,0x65,0x02,0xAA,0,1,"C665GT" }, /*08:C665GT */
+ { 0x3f0,0x44,0x44,0x0d,0x66,0x02,0xAA,0,1,"C666GT" }, /*09:C666GT */
+ { 0x3f0,0x55,0x55,0x0d,0x65,0x82,0xAA,1,2,"C665IR" }, /*10:C665IR */
+ { 0x3f0,0x44,0x44,0x0d,0x66,0x82,0xAA,1,2,"C666IR" }, /*11:C666IR */
+ { 0x3f0,0x55,0x55,0x0d,0x03,0x00,0xAA,1,3,"C669" }, /*12:C669 */
+ { 0x370,0x55,0x55,0x0d,0x03,0x00,0xAA,1,3,"C669" }, /*13:C669 at 0x370 */
+ { 0x3f0,0x55,0x55,0x0d,0x04,0x00,0xAA,1,3,"C669FR" }, /*14:C669FR */
+ { 0x370,0x55,0x55,0x0d,0x04,0x00,0xAA,1,3,"C669FR" }, /*15:C669FR at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x40,0x01,0xAA,1,4,"C67x" }, /*16:C67x */
+ { 0x370,0x55,0x55,0x20,0x40,0x01,0xAA,1,4,"C67x" }, /*17:C67x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x48,0x01,0xAA,1,4,"C68x" }, /*18:C68x */
+ { 0x370,0x55,0x55,0x20,0x48,0x01,0xAA,1,4,"C68x" }, /*19:C68x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x02,0x01,0xAA,1,4,"C93x" }, /*20:C93x */
+ { 0x370,0x55,0x55,0x20,0x02,0x01,0xAA,1,4,"C93x" }, /*21:C93x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x30,0x01,0xAA,1,4,"C93xAPM" }, /*22:C93xAPM */
+ { 0x370,0x55,0x55,0x20,0x30,0x01,0xAA,1,4,"C93xAPM" }, /*23:C93xAPM at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x03,0x01,0xAA,1,4,"C93xFR" }, /*24:C93xFR */
+ { 0x370,0x55,0x55,0x20,0x03,0x01,0xAA,1,4,"C93xFR" }, /*25:C93xFR at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x47,0x01,0xAA,1,4,"M60x" }, /*26:M60x */
+ { 0x370,0x55,0x55,0x20,0x47,0x01,0xAA,1,4,"M60x" }, /*27:M60x at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x46,0x01,0xAA,1,4,"M61x" }, /*28:M61x */
+ { 0x370,0x55,0x55,0x20,0x46,0x01,0xAA,1,4,"M61x" }, /*29:M61x at 0x370 */
+ { 0x3f0,0x55,0x55,0x0d,0x28,0x00,0xAA,1,3,"N769" }, /*30:N769FR */
+ { 0x370,0x55,0x55,0x0d,0x28,0x00,0xAA,1,3,"N769" }, /*31:N769FR at 0x370 */
+ { 0x3f0,0x55,0x55,0x20,0x09,0x01,0xAA,1,4,"N958FR" }, /*32:N958FR */
+ { 0x370,0x55,0x55,0x20,0x09,0x01,0xAA,1,4,"N958FR" }, /*33:N958FR at 0x370 */
+
+#if 0
+ /* deleted ? */
+ { 0x3f0,0x55,0x55,0x20,0x07,0x01,0xAA,1,4,"C957FR" }, /*34:C957FR */
+ { 0x370,0x55,0x55,0x20,0x07,0x01,0xAA,1,4,"C957FR" }, /*35:C957FR at 0x370 */
+ /* same ID as B80 ?? */
+ { 0x3f0,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"M70x" }, /*26:M70x */
+ { 0x370,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"M70x" }, /*27:M70x at 0x370 */
+ /* deleted ? */
+ { 0x3f0,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"M77x" }, /*28:M77x */
+ { 0x370,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"M77x" }, /*29:M77x at 0x370 */
+#endif
+#if 0
+ /* no function but detectable */
+ { 0x250,0x89,0x89,0x09,0x0a,0x01,0xAA,0,6,"W83877F"}, /*--:W83877F at 0x250 */
+ { 0x250,0x88,0x88,0x09,0x0a,0x01,0xAA,0,6,"W83877F"}, /*--:W83877F at 0x250 */
+ { 0x3F0,0x87,0x87,0x09,0x0a,0x01,0xAA,0,7,"W83877F"}, /*--:W83877F at 0x3f0 */
+ { 0x3F0,0x86,0x86,0x09,0x0a,0x01,0xAA,0,7,"W83877F"}, /*--:W83877F at 0x3f0 */
+#endif
+ { 0x250,0x89,0x89,0x09,0x0c,0x01,0xAA,2,8,"W83877TF"}, /*34:W83877TF at 0x250 */
+ { 0x250,0x88,0x88,0x09,0x0c,0x01,0xAA,2,8,"W83877TF"}, /*35:W83877TF at 0x250 */
+ { 0x3F0,0x87,0x87,0x09,0x0c,0x01,0xAA,2,9,"W83877TF"}, /*36:W83877TF at 0x3f0 */
+ { 0x3F0,0x86,0x86,0x09,0x0c,0x01,0xAA,2,9,"W83877TF"}, /*37:W83877TF at 0x3f0 */
+
+ { 0x3f0,0x87,0x87,0x20,0x97,0x71,0xAA,2,10,"W83977" }, /*38:W83977A/AF/TF/ATF at 0x3f0 */
+ { 0x370,0x87,0x87,0x20,0x97,0x71,0xAA,2,10,"W83977" }, /*39:W83977A/AF/TF/ATF at 0x370 */
+#if 0
+ /* no function but detectable */
+ { 0x02e,0x00,0x00,0x08,0x90,0x00,0xAA,3,01,"87336" }, /*--:PC87336 at 0x02e */
+ { 0x15c,0x00,0x00,0x08,0x90,0x00,0xAA,3,01,"87336" }, /*--:PC87336 at 0x15c */
+ { 0x398,0x00,0x00,0x08,0x90,0x00,0xAA,3,01,"87336" }, /*--:PC87336 at 0x398 */
+#endif
+ { 0x02e,0x00,0x00,0x20,0xa0,0x00,0xAA,3,20,"87308" }, /*40:PC87308 at 0x02e */
+ { 0x15c,0x00,0x00,0x20,0xa0,0x00,0xAA,3,20,"87308" }, /*41:PC87308 at 0x15c */
+#if defined(PC87338)
+ { 0x02e,0x00,0x00,0x08,0xb0,0x00,0xAA,3,28,"87338" }, /*42:PC87338 at 0x02e */
+ { 0x15c,0x00,0x00,0x08,0xb0,0x00,0xAA,3,28,"87338" }, /*43:PC87338 at 0x15c */
+ { 0x398,0x00,0x00,0x08,0xb0,0x00,0xAA,3,28,"87338" }, /*44:PC87338 at 0x398 */
+#endif
+ { 0x02e,0x00,0x00,0x20,0xc0,0x00,0xAA,3,22,"87307" }, /*45:PC87307 at 0x02e */
+ { 0x15c,0x00,0x00,0x20,0xc0,0x00,0xAA,3,22,"87307" }, /*46:PC87307 at 0x15c */
+ { 0x02e,0x00,0x00,0x20,0xcf,0x00,0xAA,3,21,"97307" }, /*47:PC97307 at 0x02e */
+ { 0x15c,0x00,0x00,0x20,0xcf,0x00,0xAA,3,21,"97307" }, /*48:PC97307 at 0x15c */
+ { 0x02e,0x00,0x00,0x20,0xd0,0x00,0xAA,3,22,"87317" }, /*49:PC87317 at 0x02e */
+ { 0x15c,0x00,0x00,0x20,0xd0,0x00,0xAA,3,22,"87317" }, /*50:PC87317 at 0x15c */
+ { 0x02e,0x00,0x00,0x20,0xdf,0x00,0xAA,3,21,"97317" }, /*51:PC87317 at 0x02e */
+ { 0x15c,0x00,0x00,0x20,0xdf,0x00,0xAA,3,21,"97317" }, /*52:PC87317 at 0x15c */
+ { 0x02e,0x00,0x00,0x20,0xe0,0x00,0xAA,3,23,"87309" }, /*53:PC87309 at 0x02e */
+ { 0x15c,0x00,0x00,0x20,0xe0,0x00,0xAA,3,23,"87309" }, /*54:PC87309 at 0x15c */
+ { 0x3f0,0x51,0x23,0x20,0x43,0x15,0xBB,4,30,"M1543" }, /*55:M1543 at 0x3f0 */
+ { 0x370,0x51,0x23,0x20,0x43,0x15,0xBB,4,30,"M1543" }, /*56:M1543 at 0x370 */
+ { 0, 0, 0, 0, 0, 0, 0,0, 0,"Other" }
+};
+
+#define OUTP0(x) outb(x, idnt[n].port)
+#define OUTP1(x) outb(x, idnt[n].port+1)
+#define INP1 ((unsigned char) inb(idnt[n].port+1))
+#define INP2 ((unsigned char) inb(idnt[n].port+2))
+
+static int
+hispeed_probe(int n)
+{
+ unsigned char id,rev;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ /* write ini value to enter config mode */
+ if (idnt[n].ini1) OUTP0(idnt[n].ini1);
+ if (idnt[n].ini2) OUTP0(idnt[n].ini2);
+ if (idnt[n].type != 8) {
+ OUTP0(idnt[n].idx);
+ id = INP1;
+ } else {
+ OUTP1(idnt[n].idx);
+ id = INP2;
+ }
+ switch (idnt[n].type) {
+ default:
+ OUTP0(idnt[n].idx+1);
+ rev = INP1;
+ break;
+ case 8:
+ case 9:
+ id = (unsigned char)(id&0xf);
+ rev = 0xff;
+ break;
+ case 20:
+ case 22:
+ case 28:
+ id = (unsigned char) (id & ~7);
+ rev = (unsigned char)(id & 7);
+ break;
+ case 21:
+ case 23:
+ rev = 0xff;
+ break;
+ }
+ OUTP0(idnt[n].fin); /* exit config mode */
+ restore_flags(flags);
+
+ pr_debug("*Check %s: port=%04x, ini1=%02x, ini2=%02x, idx=%02x -> id=%02x, rev=%02x (exp id=%02x, rev=%02x)\n",
+ idnt[n].name,idnt[n].port,idnt[n].ini1,idnt[n].ini2,idnt[n].idx,id,rev,
+ idnt[n].id, idnt[n].rev);
+
+ /* id & (rev or not 665IR/666IR) ? */
+ if ( (idnt[n].id==id) && ((idnt[n].rev==rev)||(idnt[n].type!=2)) && (idnt[n].type>1) )
+ return n;
+ return -1;
+}
+
+static void
+hispeed_natsemi_set(int portAdr, char opr, struct serial_state *state)
+{
+ int val;
+ if (opr == 'H') {
+ outb(0xe0, portAdr + UART_LCR); /* select Bank2 */
+#if 0
+ outb(0x41, portAdr + 0x02); /* set EXT_SL */
+#endif
+ val = inb(portAdr + 0x04); /* get EXCR2 */
+ val = (val & 0xcf) | 0x10; /* PRESL = 1.625 */
+ outb(val, portAdr + 0x04); /* set EXCR2 */
+ outb(0x00, portAdr + UART_LCR); /* select Bank0 */
+
+ state->baud_base = 921600;
+ }
+ if (opr == 'L') {
+ outb(0xe0, portAdr + UART_LCR); /* select Bank2 */
+#if 0
+ outb(0x40, portAdr + 0x02); /* clear EXT_SL */
+#endif
+ val = inb(portAdr + 0x04); /* get EXCR2 */
+ val = (val & 0xcf) | 0x00; /* PRESL = 13 */
+ outb(val, portAdr + 0x04); /* set EXCR2 */
+ outb(0x00, portAdr + UART_LCR); /* select Bank0 */
+ }
+}
+
+static int
+hispeed_operation(int n, char opr, int uart, int *portAdr, struct serial_state *state)
+{
+ unsigned char r,v,cr1,cr2;
+ int i,dvofs,nxt;
+ int portNo;
+ unsigned long flags;
+
+ portNo = 0x20 << uart;
+ v = 0;
+
+ switch(idnt[n].type) {
+ case 0: /* Unknown */
+ case 1: /* 665GT,666GT */
+ default: /* internal error.. */
+ printk( "Hispeed operation: internal error\n" );
+ break;
+ case 2: /* 665IR,666IR */
+ save_flags(flags); cli();
+ /* write ini value 2 times to enter config mode */
+ OUTP0(idnt[n].ini1);
+ OUTP0(idnt[n].ini2);
+ OUTP0(0x01);
+ cr1 = INP1;
+ OUTP0(0x02);
+ cr2 = INP1;
+ /* select sr0c */
+ OUTP0(0x0c);
+ r = INP1;
+ if (opr == 'H')
+ r |= portNo;
+ if (opr == 'L')
+ r &= ~portNo;
+ OUTP1(r);
+ r = INP1;
+ OUTP0(idnt[n].fin); /* exit config mode */
+ restore_flags(flags);
+ for (v=0x40,i=1;i<=2;++i) {
+ if (cr2 & 0x4) {
+ switch (cr2 & 0x3) {
+ case 00: portAdr[i]=0x3F8;break;
+ case 01: portAdr[i]=0x2F8;break;
+ case 02:
+ switch(cr1 & 0x30){
+ case 0x00: portAdr[i]=0x338;break;
+ case 0x10: portAdr[i]=0x3E8;break;
+ case 0x20: portAdr[i]=0x2E8;break;
+ case 0x30: portAdr[i]=0x220;break;
+ }
+ break;
+ case 03:
+ switch(cr1 & 0x30){
+ case 0x00: portAdr[i]=0x238;break;
+ case 0x10: portAdr[i]=0x2E8;break;
+ case 0x20: portAdr[i]=0x2E0;break;
+ case 0x30: portAdr[i]=0x228;break;
+ }
+ break;
+ }
+ if ( i == uart ) {
+ v = v & r;
+ break;
+ }
+ } else {
+ if ( i == uart ) {
+ v = v & r;
+ portAdr[i] = 0;
+ break;
+ }
+ }
+ cr2 >>= 4;
+ v <<= 1;
+ }
+ break;
+ case 3: /* 669 */
+ save_flags(flags); cli();
+ /* write ini value 2 times to enter config mode */
+ OUTP0(idnt[n].ini1);
+ OUTP0(idnt[n].ini2);
+ OUTP0(0x24);
+ portAdr[1] = (INP1 & 0xFE) << 2;
+ OUTP0(0x25);
+ portAdr[2] = (INP1 & 0xFE) << 2;
+ /* select sr0c */
+ OUTP0(0x0c);
+ r = INP1;
+ if (opr == 'H')
+ r |= portNo;
+ if (opr == 'L')
+ r &= ~portNo;
+ OUTP1(r);
+ r = INP1;
+ OUTP0(idnt[n].fin); /* exit config mode */
+ restore_flags(flags);
+ for (v=0x40,i=1;i<=2;++i) {
+ if (portAdr[i] & 0x300) {
+ if ( i == uart ) {
+ v = v & r;
+ break;
+ }
+ } else {
+ if ( i == uart ) {
+ v = v & r;
+ portAdr[i] = 0;
+ break;
+ }
+ }
+ v <<= 1;
+ }
+ break;
+ case 8: /* W83877TF native */
+ case 9: /* W83877TF emulate mode */
+ save_flags(flags); cli();
+ /* write ini value 2 times to enter config mode */
+ OUTP0(idnt[n].ini1);
+ if (idnt[n].type != 8)
+ OUTP0(idnt[n].ini2);
+ else
+ idnt[n].port++;
+ OUTP0(0x24);
+ portAdr[1] = (INP1 & 0xFE) << 2;
+ OUTP0(0x25);
+ portAdr[2] = (INP1 & 0xFE) << 2;
+ /* select sr19 */
+ OUTP0(0x19);
+ r = INP1;
+ v = 0;
+ if (portNo & 0x40)
+ v = 0x2;
+ if (portNo & 0x80)
+ v |= 0x1;
+ if (opr == 'H')
+ r |= v;
+ if (opr == 'L')
+ r &= ~v;
+ OUTP1(r);
+ r = INP1;
+ OUTP0(idnt[n].fin); /* exit config mode */
+ restore_flags(flags); /* sti */
+ for (v=0x02,i=1;i<=2;++i) {
+ if (portAdr[i] & 0x300) {
+ if ( i == uart ) {
+ v = v & r;
+ if ( v )
+ state->baud_base = 921600;
+ break;
+ }
+ } else {
+ if ( i == uart ) {
+ v = v & r;
+ portAdr[i] = 0;
+ break;
+ }
+ }
+ v >>= 1;
+ }
+ if (idnt[n].type == 8)
+ idnt[n].port--;
+ break;
+ case 4: /* 67x,68x,93x,957 dev 4,5*/
+ case 30: /* ALI M1543 dev 4,5*/
+ case 10: /* W83977TF dev 2,3*/
+ case 20: /* NS PC8xxxxx dev 6,5*/
+ case 21: /* NS PC9xxxxx dev 6,5*/
+ case 22: /* NS PC8xxxxx dev 6,5*/
+ case 23: /* NS PC9xxxxx dev 3,2*/
+ switch(idnt[n].type) {
+ case 20:
+ case 21:
+ case 22:
+ dvofs = 6; /* 5:port2, 6:port1 87307/97037/87308/87317/97317 */
+ nxt = -1;
+ break;
+ case 23:
+ dvofs = 3; /* 2:port2, 3:port1 87309 */
+ nxt = -1;
+ break;
+ case 10: /* WinBond */
+ dvofs = 2; /* 2:port1, 3:port2 */
+ nxt = 1;
+ break;
+ default: /* SMC,ALI */
+ dvofs = 4; /* 4:port1, 5:port2 */
+ nxt = 1;
+ break;
+ }
+
+ for (i = 1; i <=2 ; ++i, dvofs += nxt) {
+ save_flags(flags); cli();
+ /* write ini value 2 times to enter config mode */
+ if (idnt[n].ini1) OUTP0(idnt[n].ini1);
+ if (idnt[n].ini2) OUTP0(idnt[n].ini2);
+ OUTP0(0x07);/* Select device register */
+ OUTP1(dvofs); /* Set device value */
+ OUTP0(0x60); /* get port addr. */
+ portAdr[i] = ((int) INP1) << 8;
+ OUTP0(0x61);
+ portAdr[i] |= (int) INP1;
+#if defined(CHKENB)
+ OUTP0(0x30); /* enabled ? */
+ if (!INP1)
+ portAdr[i] = 0;
+#endif
+ OUTP1(0xF0);/* Select serial config register */
+ r = INP1;
+ switch (idnt[n].type) {
+ case 4: /* SMC */
+ case 30: /* ALI */
+ if ((0x20 << i) & portNo) {
+ if (opr == 'H')
+ r |= 2;
+ if (opr == 'L')
+ r &= ~2;
+ OUTP1(r);
+ }
+ r = INP1;
+ v = (unsigned char)((r&0x3) == 0x2);
+ break;
+ case 10: /* Winbond 83977TF */
+ if ((0x20 << i) & portNo) {
+ if (opr == 'H')
+ r |= 3;
+ if (opr == 'L')
+ r &= ~3;
+ OUTP1(r);
+ }
+ r = INP1;
+ v = (unsigned char)((r&0x3) == 0x3);
+ if ( v )
+ state->baud_base = 921600;
+ break;
+ case 20: /* NS 87308 */
+ case 21: /* NS */
+ case 22: /* NS */
+ case 23: /* NS */
+ if ((0x20 << i) & portNo) {
+ OUTP0(0xf0);
+ OUTP1(r|0x80); /* enable Bank Select */
+ }
+ v = (unsigned char)(INP1&0x80);
+ if (v) hispeed_natsemi_set(portAdr[i], opr, state);
+ if ((0x20 << i) & portNo) {
+ if (opr == 'L') {
+ OUTP0(0xf0);
+ OUTP1(r&0x7f); /* disable Bank Select */
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ OUTP0(idnt[n].fin); /* exit config mode */
+ restore_flags(flags); /* sti */
+ if (portAdr[i]) {
+ if ( i == uart ) {
+ break;
+ }
+ } else {
+ if ( i == uart ) {
+ portAdr[i] = 0;
+ break;
+ }
+ }
+ }
+ break;
+#ifdef PC87338
+ case 28: /* PC87338 */
+ OUTP0(0x00); /* get FER 3:FDC 2:SCC2 1:SCC1 0:PPA */
+ cr1 = INP1;
+ OUTP0(0x01); /* get FAR 7:6-COM34 5:4-SCC2 3:2-SCC1 2:1-PPA */
+ cr2 = INP1;
+
+ for (i=1;i<=2;++i) {
+ if ((cr1 & (1 << i) )==0) {
+ portAdr[i] = 0;
+ if ( i == uart ) {
+ v = 0;
+ break;
+ }
+ continue;
+ }
+ switch ((cr2>>(2*i)) & 0x03) {
+ case 00: portAdr[i]=0x3F8;break;
+ case 01: portAdr[i]=0x2F8;break;
+ case 02: /* com3 */
+ switch(cr2 & 0xc0){
+ case 0x00: portAdr[i]=0x3E8;break;
+ case 0x40: portAdr[i]=0x338;break;
+ case 0x80: portAdr[i]=0x2E8;break;
+ case 0xc0: portAdr[i]=0x220;break;
+ }
+ break;
+ case 03: /* com4 */
+ switch(cr2 & 0xc0){
+ case 0x00: portAdr[i]=0x2E8;break;
+ case 0x40: portAdr[i]=0x238;break;
+ case 0x80: portAdr[i]=0x2E0;break;
+ case 0xc0: portAdr[i]=0x228;break;
+ }
+ break;
+ }
+ if ((0x20 << i) & portNo) {
+ OUTP0(0x40); /* enable Bank Select */
+ OUTP1(INP1 | (0x10 << (3*(i-1))) );
+ }
+ v = (unsigned char)(INP1 & (0x10 << (3*(i-1))) );
+ if ( v ) hispeed_natsemi_set(portAdr[i], opr, state);
+ if ((0x20 << i) & portNo) {
+ if (opr == 'L') {
+ OUTP0(0x40); /* disable Bank Select */
+ OUTP1(INP1 & ~(0x10 << (3*(i-1))) );
+ }
+ }
+ if ( i == uart )
+ break;
+ }
+ break;
+#endif
+ }
+
+ return v;
+}
+
+
+static void
+get_portAdr(int n, int *portAdr, struct serial_state *state)
+{
+ hispeed_operation(n, ' ', 2, portAdr, state);
+}
+
+
+int
+enable_hispeed(struct serial_state *state)
+{
+ int result, n;
+ int uart;
+ char *fab;
+ int portAdr[3];
+
+ printk( "Detecting hispeed serials (%04x)... ", state->port );
+
+ /* find what type of controller is used */
+ for (n = 0; idnt[n].port; n++)
+ if ( hispeed_probe(n) != -1 ) break;
+
+ if ( idnt[n].port == 0 ) {
+ printk( "not there\n" );
+ return 0;
+ }
+
+ /* what port? */
+ get_portAdr( n, portAdr, state );
+
+ uart = 0;
+ if ( portAdr[1] == state->port )
+ uart |= 1;
+ if ( portAdr[2] == state->port )
+ uart |= 2;
+
+ if ( uart == 0 || uart > 2) {
+ printk( "not for this port\n" );
+ return 0;
+ }
+
+ switch(idnt[n].pch) {
+ case 1:
+ fab = "SMC37";
+ break;
+ case 2:
+ fab = "Winbond ";
+ break;
+ case 3:
+ fab = "NS PC";
+ break;
+ case 4:
+ fab = "ALI ";
+ break;
+ default:
+ fab = "nowhere ";
+ break;
+ }
+ printk("%s%s at %04xh",fab,idnt[n].name,idnt[n].port);
+
+ /* try to enable High Speed mode */
+ result = hispeed_operation(n, 'H', uart, portAdr, state);
+ printk(", base = %d baud", state->baud_base);
+ return result;
+}
+
+#ifdef MODULE
+void
+disable_hispeed(struct serial_state *state)
+{
+ int n;
+ int uart;
+ int portAdr[3];
+
+ {
+ if (state->type != PORT_16550A )
+ return;
+
+ /* find what type of controller is used */
+ for (n = 0; idnt[n].port; n++) {
+ if ( hispeed_probe(n) != -1 ) break;
+ }
+ if ( idnt[n].port == 0 )
+ return;
+
+ /* what port? */
+ get_portAdr( n, portAdr, state );
+
+ uart = 0;
+ if ( portAdr[1] == state->port )
+ uart |= 1;
+ if ( portAdr[2] == state->port )
+ uart |= 2;
+
+ if ( uart == 0 || uart > 2)
+ return;
+
+ /* try to disable High Speed mode */
+ hispeed_operation(n, 'L', uart, portAdr, state);
+ }
+}
+#endif /* MODULE */

Hmm, here's some obsolete docs...

Serial High Speed Mode Patch for Linux

June 15, 1997
MIZUHARA Bun
(mizuhara@st.rim.or.jp)

Sep 7, 1998
Updated by TAKEUCHI Yoji
(ytakeuch@po.iijnet.or.jp)

1. Introduction

Some SMC/NS/Winbond Multi-I/O chips and ALI south bridge chips have High
Speed mode, in which baud rate can be set to 230400, 460800 (or 921600
at NS/Winbond chips) bps. With this patch applied, the serial port driver
automatically probe those chips and enable High Speed mode.

This patch is created and tested for kernel 2.0.34.

There is absolutely no warranty for this software.
This software can be distributed or modified freely, provided that this
documentation accompanies.
However, it is encouraged to send comments or modifications to the author
(mizuhara@st.rim.or.jp).

Copyright 1996,1997 MIZUHARA Bun (mizuhara@st.rim.or.jp).

2. History

Spetember 7, 1998 Updated to V1.7a.
Updated features from original Shsmod V1.7a.

June 15, 1997 Updated to v1.1.
Fixed a bug in 669 probing. Added support for new chips.

November 3, 1996 Released first version.
(My birthday!)

3. System requirements

OS: Linux kernel version 2.0.34 or later.

I/O Chips:
ALI:
M1543(Aladdin V south bridge)

Serial ports provided by this chip will run at 230400 and 460800 bps.

NS:
PC87308
PC87307/87317
PC97307/97317
PC87309
PC87338

Serial ports provided by these chips will run at 230400/460800/921600(not tested) bps.

SMC:
37C665IR/666IR
37C669/669FR,37N769
37B72x/77x/78x/80x,37C67x/68x/93x/93xAPM/93xFR,37M60x/61x,37N958FR

Serial ports provided by these chips will run at 230400 and 460800 bps.

WinBond:
W83877TF(TF ONLY, A/F/AF NOT supported)
W83977A/AF/TF/ATF

Serial ports provided by these chips will run at 230400 and 460800 bps.

Visit http://www.yk.rim.or.jp/~gigo/download.html for detailed information
and availability of patches for other OS.

4. Installation and testing

(1) Login as root.

(2) Change directory to /usr/src (or a directory where linux kernel sources
reside).

(3) Apply this patch.

# patch -p -s < shsmod17a-linux.patch

No messages should be displayed.

(4) Rebuild the kernel.

If serial driver is compiled in the kernel (and LILO is used as a boot
loader), follow these steps:

# cd linux
# make zlilo

If serial driver is compiled as a module, follow these steps:

# cd linux
# make modules
# make modules_install

(5) Reboot with the new kernel and watch messages carefully.

If you find messages like this, congratulations!

Serial driver version 4.13 with HighSpeedMode V1.7a(Linux 06-Sep-98) enabled
tty00 at 0x03f8 (irq = 4) is a 16550A(NS PC87308 at 002eh, Serial Speed Mode: High)
tty01 at 0x02f8 (irq = 3) is a 16550A(NS PC87308 at 002eh, Serial Speed Mode: High)

The first line shows that the driver supports High Speed mode.
It does not mean a chip used in your system supports High Speed mode.
The second and third line shows I/O chip type and wheather it supports
High Speed mode. If you cannot find "High Speed mode enabled" in the
messages, your chip does not support High Speed mode.
Try another motherboard or I/O card.

5. How to use serial speed higher than 38400bps

From application's point of view, maximum speed of serial port is 38400bps
even if High Speed mode is enabled. Set it to 38400bps, and call a program
named "setserial" in order to use higher speed.

# setserial /dev/cua1 spd_hi
sets /dev/cua1 (com2) to 57600bps.

# setserial /dev/cua0 spd_vhi
sets /dev/cua0 (com1) to 115200bps.

for SMC/ALI chips
# setserial /dev/cua1 spd_cust divisor 0x8002
sets /dev/cua1 (com2) to 230400bps (with High Speed mode enabled).

# setserial /dev/cua0 spd_cust divisor 0x8001
sets /dev/cua0 (com1) to 460800bps (with High Speed mode enabled).

for NS/Winbond chips
# setserial /dev/cua1 spd_cust divisor 4
sets /dev/cua1 (com2) to 230400bps (with High Speed mode enabled).

# setserial /dev/cua0 spd_cust divisor 2
sets /dev/cua0 (com1) to 460800bps (with High Speed mode enabled).

# setserial /dev/cua0 spd_cust divisor 1
sets /dev/cua0 (com1) to 921600bps (with High Speed mode enabled).
!!! THIS MODE IS NOT TESTED !!!

6. Bug reports

If you encounter problems, follow these steps.

(1) Recompile the driver with DEBUG_CONFIG_SHSMOD option enabled, if
possible. In the file linux/drivers/char/serial.c, you can find

/* #define DEBUG_CONFIG_SHSMOD */

at line number around 80. Uncomment this line and recompile, as
shown above.

(2) Reboot with the kernel, and watch messages. Write down relevant
messages. You can scroll up the screen by pressing Shift-PageUp.

(3) E-mail to me (ytakeuch@po.iijnet.or.jp) with detailed description
of the symptom, kernel messages, and hardware environment.

-- 
I'm really pavel@atrey.karlin.mff.cuni.cz. 	   Pavel
Look at http://atrey.karlin.mff.cuni.cz/~pavel/ ;-).

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/