High speed serial ports

Pavel Machek (pavel@bug.ucw.cz)
Tue, 11 May 1999 00:23:08 +0200


Hi!

Of course you want to use full speed of your serial port (up-to
~1Mbit), do you?

Ok, here's the patch you are interested in.

Pavel
PS: tytso, is there chance for this to be included in next version? Is
this still too dirty? (Well, do not look at #warning, if you say it
looks good I'm sure I can find a way for it to go away...)

--- clean/drivers/char/Config.in Sat Apr 17 22:31:44 1999
+++ linux/drivers/char/Config.in Sat Apr 17 19:02:53 1999
@@ -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
--- clean/drivers/char/serial.c Fri Mar 26 17:46:12 1999
+++ linux/drivers/char/serial.c Fri Mar 26 17:30:53 1999
@@ -1376,7 +1376,11 @@
serial_outp(info, UART_EFR,
(cflag & CRTSCTS) ? UART_EFR_CTS : 0);
}
- serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+#warning FIXME
+ if (0)
+ serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ else
+ serial_outp(info, UART_LCR, 0xe0); /* select Bank 2 */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
if (info->state->type == PORT_16750)
@@ -2751,6 +2755,7 @@
ret += sprintf(buf+ret, " baud:%d",
state->baud_base / info->quot);
}
+ ret += sprintf(buf+ret, " basebaud:%d", state->baud_base);

ret += sprintf(buf+ret, " tx:%d rx:%d",
state->icount.tx, state->icount.rx);
@@ -3035,6 +3040,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) {
@@ -3312,6 +3321,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 */

--- /dev/null Tue Jul 21 02:45:36 1998
+++ linux/drivers/char/hispeed.c Sun Dec 20 14:39:26 1998
@@ -0,0 +1,663 @@
+/*
+ * 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 */
+
+ printk( "Setting PC87338 @ %x to hispeed mode\n", portAdr );
+ 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\n", 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 */

-- 
I'm really pavel@ucw.cz. Look at http://195.113.31.123/~pavel.          Pavel
Hi! I'm a .signature virus! Copy me into your ~/.signature to help me spread!

- 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/