* [patch] serial.c ALI/SMSC/VIA high speed support
@ 2001-08-26 12:54 Marek Michalkiewicz
2001-08-30 18:59 ` Theodore Tso
0 siblings, 1 reply; 4+ messages in thread
From: Marek Michalkiewicz @ 2001-08-26 12:54 UTC (permalink / raw)
To: linux-kernel
I was a bit surprised when I learned that _many_ motherboards
support high speed (usually max 460800 bps) serial ports, but
this fact is not advertised in any motherboard manuals!
This patch adds support for high speed mode (baud_base*2 and *4
by using divisors 0x8002 and 0x8001) supported by many UARTs
integrated in motherboard chipsets. VIA VT82C686 is detected
(easy PCI probe) and high speed mode is enabled automatically.
No detection of other chips for now, because there are so many
and ISA probes are potentially unsafe. But you can enable the
high speed mode from a simple user level program you can write
after looking at the datasheet for the chip on your motherboard.
Some chips also need setting baud_base to 921600 with setserial.
Without this patch, you could still use spd_cust to set custom
divisor 0x8002, but the timeout was much too long, baud rate
was not reported correctly in /proc, etc. Also, this patch
fixes possible 32-bit integer overflow in timeout calculation.
Earlier version of this patch (without VT82C686 detection)
was sent to the linux-serial list, which does not seem to be
very active. Comments and suggestions are welcome.
Some places to look at:
http://www.devdrv.com/shsmod/ - DOS/Win* high speed mode
enabler, old Linux (2.0.x and 2.2.x) and FreeBSD patches.
Some people in Japan discovered all this a few years before
I did... (Unfortunately, latest version of this program
is closed source Windows-only, but old sources are still
available.)
http://www.kati.fi/viahss/ - this is where I found the code to
enable high speed mode on the VT82C686. (Unfortunately, VIA
does not make the full datasheet available for download.)
Since there is a lot of hardware that may benefit from this
(potentially almost all current x86 Linux boxes in fact -
not old 386/486 but quite possibly newer 486 too), I'd like
to see this (after more testing) in the standard kernel.
Thanks,
Marek
--- serial.c.orig Tue Aug 14 01:37:33 2001
+++ serial.c Sun Aug 26 14:32:41 2001
@@ -57,6 +57,8 @@
* 10/00: add in optional software flow control for serial console.
* Kanoj Sarcar <kanoj@sgi.com> (Modified by Theodore Ts'o)
*
+ * 08/01: initial support for high speed mode on ALI/SMSC/VIA chips.
+ * Marek Michalkiewicz <marekm@amelek.gda.pl>
*/
static char *serial_version = "5.05c";
@@ -1601,6 +1603,62 @@
#endif
/*
+ * Special divisor values may be used for (otherwise 16550A compatible)
+ * UARTs in ALI/SMSC/VIA super I/O chips found on _many_ motherboards.
+ * Other popular chips (NSC/Winbond) can support high speeds by simply
+ * changing the UART clock (baud_base 921600 instead of 115200).
+ *
+ * Note that the serial driver itself does not enable high speed mode.
+ * This can be done by a separate program, specific to the chip type.
+ */
+
+static int baud_from_quot(int baud_base, int quot)
+{
+ int baud = 0;
+
+ if (quot == 0x8001)
+ baud = baud_base * 4;
+ else if (quot == 0x8002)
+ baud = baud_base * 2;
+ else if (quot > 0)
+ baud = baud_base / quot;
+ return baud;
+}
+
+static int quot_from_baud(int baud_base, int baud)
+{
+ int quot = 0;
+
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*baud_base / 269);
+ else if (baud == baud_base * 4)
+ quot = 0x8001;
+ else if (baud == baud_base * 2)
+ quot = 0x8002;
+ else if (baud > 0)
+ quot = baud_base / baud;
+ return quot;
+}
+
+static int timeout_from_quot(int fifo_bits, int quot, int baud_base)
+{
+ int timeout;
+
+ if (quot == 0x8001)
+ timeout = (fifo_bits * HZ) / (baud_base * 4);
+ else if (quot == 0x8002)
+ timeout = (fifo_bits * HZ) / (baud_base * 2);
+ else if ((fifo_bits * quot) > (INT_MAX / HZ))
+ /* Avoid potential 32-bit integer overflow. */
+ timeout = ((HZ * quot) / baud_base) * fifo_bits;
+ else
+ timeout = (fifo_bits * HZ * quot) / baud_base;
+
+ return timeout + HZ/50; /* Add .02 seconds of slop. */
+}
+
+/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
@@ -1668,13 +1726,9 @@
if (baud == 38400 &&
((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
quot = info->state->custom_divisor;
- else {
- if (baud == 134)
- /* Special case since 134 is really 134.5 */
- quot = (2*baud_base / 269);
- else if (baud)
- quot = baud_base / baud;
- }
+ else if (baud)
+ quot = quot_from_baud(baud_base, baud);
+
/* If the quotient is zero refuse the change */
if (!quot && old_termios) {
info->tty->termios->c_cflag &= ~CBAUD;
@@ -1685,17 +1739,12 @@
if (baud == 38400 &&
((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
quot = info->state->custom_divisor;
- else {
- if (baud == 134)
- /* Special case since 134 is really 134.5 */
- quot = (2*baud_base / 269);
- else if (baud)
- quot = baud_base / baud;
- }
+ else if (baud)
+ quot = quot_from_baud(baud_base, baud);
}
/* As a last resort, if the quotient is zero, default to 9600 bps */
if (!quot)
- quot = baud_base / 9600;
+ quot = quot_from_baud(baud_base, 9600);
/*
* Work around a bug in the Oxford Semiconductor 952 rev B
* chip which causes it to seriously miscalculate baud rates
@@ -1706,12 +1755,12 @@
quot++;
info->quot = quot;
- info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
- info->timeout += HZ/50; /* Add .02 seconds of slop */
+ info->timeout = timeout_from_quot(info->xmit_fifo_size * bits,
+ quot, baud_base);
/* Set up FIFO's */
if (uart_config[info->state->type].flags & UART_USE_FIFO) {
- if ((info->state->baud_base / quot) < 2400)
+ if (baud_from_quot(info->state->baud_base, quot) < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
#ifdef CONFIG_SERIAL_RSA
else if (info->state->type == PORT_RSA)
@@ -3288,8 +3337,8 @@
strcat(stat_buf, "|RI");
if (info->quot) {
- ret += sprintf(buf+ret, " baud:%d",
- state->baud_base / info->quot);
+ int baud = baud_from_quot(state->baud_base, info->quot);
+ ret += sprintf(buf+ret, " baud:%d", baud);
}
ret += sprintf(buf+ret, " tx:%d rx:%d",
@@ -5335,6 +5384,41 @@
#endif /* ENABLE_SERIAL_PNP */
+
+static void __init enable_high_speed_mode(void)
+{
+#ifdef ENABLE_SERIAL_PCI
+ struct pci_dev *pcidev;
+ unsigned char confval, val;
+
+ /*
+ * Based on the VT82C686[AB] high speed serial port enabler
+ * by Juhani Rautiainen <jrauti@iki.fi>.
+ */
+ pcidev = pci_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_82C686,
+ NULL);
+ if (pcidev) {
+ cli();
+ pci_read_config_byte(pcidev, 0x85, &confval);
+ pci_write_config_byte(pcidev, 0x85, confval | 0x02);
+ outb(0xEE, 0x3F0);
+ val = inb(0x3F1);
+ val |= 0xC0; /* high speed mode (divisor > 0x8000) on both UARTs */
+ outb(0xEE, 0x3F0);
+ outb(val, 0x3F1);
+ pci_write_config_byte(pcidev, 0x85, confval);
+ sti();
+ printk(KERN_INFO "VIA VT82C686[AB] serial port high speed mode enabled\n");
+ }
+#endif
+ /*
+ * TODO: enable high speed mode on ALI/SMSC (divisor > 0x8000)
+ * and NSC/Winbond (baud_base 921600) chips - probably should be
+ * optional, because of potentially unsafe ISA I/O port probes.
+ */
+}
+
/*
* The serial driver boot-time initialization code!
*/
@@ -5485,6 +5569,7 @@
tty_register_devfs(&callout_driver, 0,
callout_driver.minor_start + state->line);
}
+ enable_high_speed_mode();
#ifdef ENABLE_SERIAL_PCI
probe_serial_pci();
#endif
@@ -5935,7 +6020,7 @@
info->io_type = state->io_type;
info->iomem_base = state->iomem_base;
info->iomem_reg_shift = state->iomem_reg_shift;
- quot = state->baud_base / baud;
+ quot = quot_from_baud(state->baud_base, baud);
cval = cflag & (CSIZE | CSTOPB);
#if defined(__powerpc__) || defined(__alpha__)
cval >>= 8;
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [patch] serial.c ALI/SMSC/VIA high speed support
2001-08-26 12:54 [patch] serial.c ALI/SMSC/VIA high speed support Marek Michalkiewicz
@ 2001-08-30 18:59 ` Theodore Tso
2001-08-31 14:52 ` Marek Michalkiewicz
0 siblings, 1 reply; 4+ messages in thread
From: Theodore Tso @ 2001-08-30 18:59 UTC (permalink / raw)
To: Marek Michalkiewicz; +Cc: linux-kernel
On Sun, Aug 26, 2001 at 02:54:42PM +0200, Marek Michalkiewicz wrote:
> I was a bit surprised when I learned that _many_ motherboards
> support high speed (usually max 460800 bps) serial ports, but
> this fact is not advertised in any motherboard manuals!
This patch hard codes magic divisor values for a specific motherboard
into the serial driver. The fact that the motherboard is using magic
divisor values is in incredible bad taste (unlike the motherboards
that simply use a faster clock frequency and so simply require
different base baud value), but the fact that the patch uncoditionally
recognizes these magic values and changes the behaviour for all UART's
unconditionally (not just for the motherbards that use this completely
broken design) is in very bad taste....
If you're going to do something like this, then it must be conditional
on a UART type that indicates that this is a broken UART that is
playing wierd shit divisor games. Please don't do this
unconditionally, since then when the next broken motherboard design
uses another set of magic divisor numbers (which possibly might
overlap with VIA's broken magic divisor numbers), the result will be a
gigantic mess.....
- Ted
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [patch] serial.c ALI/SMSC/VIA high speed support
2001-08-30 18:59 ` Theodore Tso
@ 2001-08-31 14:52 ` Marek Michalkiewicz
0 siblings, 0 replies; 4+ messages in thread
From: Marek Michalkiewicz @ 2001-08-31 14:52 UTC (permalink / raw)
To: Theodore Tso; +Cc: Marek Michalkiewicz, linux-kernel
Theodore Tso wrote:
> This patch hard codes magic divisor values for a specific motherboard
It's not just one specific motherboard - several chips from different
manufacturers use exactly the same magic divisor values, so this looks
like some sort of "industry standard". The patch includes VT82C686A
detection simply because it's a reasonably safe PCI probe. Other chips
(SMSC, ALI) would need more dangerous ISA probes, but once you know what
chip you have, high speed mode can be enabled even from user space, and
the magic divisors will work the same way.
> into the serial driver. The fact that the motherboard is using magic
> divisor values is in incredible bad taste (unlike the motherboards
I guess they did it that way for DOS/Win* backwards compatibility -
lower speeds (max 115200 bps) work as usual, so the high speed mode
could even be enabled by the BIOS if it cared enough. These magic
values correspond to not very often used baud rates, about 3.5 bps ;)
Other chips (NSC/Winbond) use baud_base 921600 which is cleaner but
not very backwards-compatible...
> If you're going to do something like this, then it must be conditional
This would require a new version of setserial that recognizes a new
UART type (16550A with magic divisors). If you can release it, I see
no problem with making magic divisors conditional as you suggest.
One more thing - these special features can't be auto-detected by
simply looking at UART registers. These chips have programmable
UART I/O base and IRQ which don't have to be standard COM1/COM2
(or even COM3/COM4 for that matter - only the BIOS setup limits your
choices, the chip allows any multiple of 8 as UART I/O base).
Now, I can read the two I/O base addresses from the chip, but what to
do next? Should I search rs_table[] for a matching I/O port address
to change UART type (ALI/SMSC/VIA) or baud_base (NSC/Winbond) there?
What if not found (non-standard I/O base set by the BIOS), should a new
entry be added after the standard ones like for PCI ports?
Of course, auto-detection is not something very important right now, but
I'd like to see basic support for magic divisors (spd_cust is a kludge,
the driver thinks the speed is very low, sets wrong FIFO trigger level
and much too long timeout) at least for people who know how to enable
high speed mode in their chips...
Thanks,
Marek
^ permalink raw reply [flat|nested] 4+ messages in thread
[parent not found: <Pine.LNX.4.30.0108261918450.23071-101000@anime.net>]
end of thread, other threads:[~2001-08-31 15:01 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2001-08-26 12:54 [patch] serial.c ALI/SMSC/VIA high speed support Marek Michalkiewicz
2001-08-30 18:59 ` Theodore Tso
2001-08-31 14:52 ` Marek Michalkiewicz
[not found] <Pine.LNX.4.30.0108261918450.23071-101000@anime.net>
2001-08-27 9:41 ` Marek Michalkiewicz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox