public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] 8250.c: Fix to make 16C950 UARTs work
@ 2005-09-09  1:31 Mathias Adam
  2005-09-09  1:58 ` Stefan Smietanowski
  0 siblings, 1 reply; 6+ messages in thread
From: Mathias Adam @ 2005-09-09  1:31 UTC (permalink / raw)
  To: linux-kernel; +Cc: rmk+serial

Currently serial8250_set_termios() refuses to program a baud rate larger
than uartclk/16. However the 16C950 supports baud rates up to uartclk/4.
This worked already with Linux 2.4 so the biggest part of this patch was
simply taken from there and adapted to 2.6.

I needed this to get a Socket Bluetooth CF Card to work with BlueZ under
2.6 (the card did work under 2.4 already).

I posted the patch a while ago on the BlueZ mailing list and got reports
that it works as it should for a number of people so one could consider
including it into the standard kernel - opinions?

Please CC me as I'm not subscribed to the list.

Mathias Adam


--- linux-2.6.13-org/drivers/serial/8250.c	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/8250.c	2005-09-09 02:16:49.000000000 +0200
@@ -1665,7 +1665,7 @@
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
-	unsigned int baud, quot;
+	unsigned int baud, quot, max_baud;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -1697,9 +1697,28 @@
 	/*
 	 * Ask the core to calculate the divisor for us.
 	 */
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	MAX_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+	baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
 	quot = serial8250_get_divisor(port, baud);
 
+	/* 
+	 * 16C950 supports additional prescaler ratios between 1:16 and 1:4
+	 * thus increasing max baud rate to uartclk/4. The following was taken
+	 * from kernel 2.4 by Mathias Adam <a2@adamis.de> to make the Socket
+	 * Bluetooth CF Card work under 2.6.13.
+	 */
+	if (up->port.type == PORT_16C950) {
+		unsigned int baud_base = port->uartclk/16;
+		if (baud <= port->uartclk/16)
+			serial_icr_write(up, UART_TCR, 0);
+		else if (baud <= port->uartclk/8) {
+			serial_icr_write(up, UART_TCR, 0x8);
+		} else if (baud <= port->uartclk/4) {
+			serial_icr_write(up, UART_TCR, 0x4);
+		} else
+			serial_icr_write(up, UART_TCR, 0);
+	}
+	
 	/*
 	 * Oxford Semi 952 rev B workaround
 	 */

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] 8250.c: Fix to make 16C950 UARTs work
  2005-09-09  1:31 [PATCH] 8250.c: Fix to make 16C950 UARTs work Mathias Adam
@ 2005-09-09  1:58 ` Stefan Smietanowski
  2005-09-09  2:49   ` Mathias Adam
  0 siblings, 1 reply; 6+ messages in thread
From: Stefan Smietanowski @ 2005-09-09  1:58 UTC (permalink / raw)
  To: Mathias Adam; +Cc: linux-kernel, rmk+serial

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Mathias Adam wrote:
> Currently serial8250_set_termios() refuses to program a baud rate larger
> than uartclk/16. However the 16C950 supports baud rates up to uartclk/4.
> This worked already with Linux 2.4 so the biggest part of this patch was
> simply taken from there and adapted to 2.6.
> -	unsigned int baud, quot;
> +	unsigned int baud, quot, max_baud;
                                 ^^^^^^^^
> -	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
> +	MAX_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
        ^^^^^^^^

Did you even compile test this?

// Stefan
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (MingW32)

iD8DBQFDIOxFBrn2kJu9P78RAnG3AJ9EJKl6q4Q4+jXRdMifvmOEdO+HewCfUPd8
T2qQREDAgUq2C7j9yfaPemQ=
=hGK0
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] 8250.c: Fix to make 16C950 UARTs work
  2005-09-09  1:58 ` Stefan Smietanowski
@ 2005-09-09  2:49   ` Mathias Adam
  2005-09-09 10:18     ` Russell King
  0 siblings, 1 reply; 6+ messages in thread
From: Mathias Adam @ 2005-09-09  2:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: rmk+serial

Stefan Smietanowski wrote:
> Mathias Adam wrote:
> > Currently serial8250_set_termios() refuses to program a baud rate larger
> > than uartclk/16. However the 16C950 supports baud rates up to uartclk/4.
> > This worked already with Linux 2.4 so the biggest part of this patch was
> > simply taken from there and adapted to 2.6.
> > -	unsigned int baud, quot;
> > +	unsigned int baud, quot, max_baud;
>                                ^^^^^^^^
> > -	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
> > +	MAX_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
>       ^^^^^^^^
> 
> Did you even compile test this?

Oops, I really really wonder how THIS could have happened as I just attached
my existing patch file (which was and still is correct) without touching
it - at least that's what I thought...  Sorry!


Mathias Adam

I hope everything's alright this time:

--- linux-2.6.13-org/drivers/serial/8250.c	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/8250.c	2005-09-09 02:16:49.000000000 +0200
@@ -1665,7 +1665,7 @@
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
-	unsigned int baud, quot;
+	unsigned int baud, quot, max_baud;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -1697,9 +1697,28 @@
 	/*
 	 * Ask the core to calculate the divisor for us.
 	 */
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+	baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
 	quot = serial8250_get_divisor(port, baud);
 
+	/* 
+	 * 16C950 supports additional prescaler ratios between 1:16 and 1:4
+	 * thus increasing max baud rate to uartclk/4. The following was taken
+	 * from kernel 2.4 by Mathias Adam <a2@adamis.de> to make the Socket
+	 * Bluetooth CF Card work under 2.6.13.
+	 */
+	if (up->port.type == PORT_16C950) {
+		unsigned int baud_base = port->uartclk/16;
+		if (baud <= port->uartclk/16)
+			serial_icr_write(up, UART_TCR, 0);
+		else if (baud <= port->uartclk/8) {
+			serial_icr_write(up, UART_TCR, 0x8);
+		} else if (baud <= port->uartclk/4) {
+			serial_icr_write(up, UART_TCR, 0x4);
+		} else
+			serial_icr_write(up, UART_TCR, 0);
+	}
+	
 	/*
 	 * Oxford Semi 952 rev B workaround
 	 */

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] 8250.c: Fix to make 16C950 UARTs work
  2005-09-09  2:49   ` Mathias Adam
@ 2005-09-09 10:18     ` Russell King
  2005-09-09 14:42       ` Mathias Adam
  2005-09-16 12:11       ` Mathias Adam
  0 siblings, 2 replies; 6+ messages in thread
From: Russell King @ 2005-09-09 10:18 UTC (permalink / raw)
  To: Mathias Adam; +Cc: linux-kernel

A couple of comments - see below.

On Fri, Sep 09, 2005 at 04:49:27AM +0200, Mathias Adam wrote:
> --- linux-2.6.13-org/drivers/serial/8250.c	2005-08-29 01:41:01.000000000 +0200
> +++ linux-2.6.13/drivers/serial/8250.c	2005-09-09 02:16:49.000000000 +0200
> @@ -1665,7 +1665,7 @@
>  	struct uart_8250_port *up = (struct uart_8250_port *)port;
>  	unsigned char cval, fcr = 0;
>  	unsigned long flags;
> -	unsigned int baud, quot;
> +	unsigned int baud, quot, max_baud;
>  
>  	switch (termios->c_cflag & CSIZE) {
>  	case CS5:
> @@ -1697,9 +1697,28 @@
>  	/*
>  	 * Ask the core to calculate the divisor for us.
>  	 */
> -	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
> +	max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
> +	baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
>  	quot = serial8250_get_divisor(port, baud);
>  
> +	/* 
> +	 * 16C950 supports additional prescaler ratios between 1:16 and 1:4
> +	 * thus increasing max baud rate to uartclk/4. The following was taken
> +	 * from kernel 2.4 by Mathias Adam <a2@adamis.de> to make the Socket
> +	 * Bluetooth CF Card work under 2.6.13.
> +	 */
> +	if (up->port.type == PORT_16C950) {
> +		unsigned int baud_base = port->uartclk/16;

baud_base appears unused.

> +		if (baud <= port->uartclk/16)
> +			serial_icr_write(up, UART_TCR, 0);
> +		else if (baud <= port->uartclk/8) {
> +			serial_icr_write(up, UART_TCR, 0x8);
> +		} else if (baud <= port->uartclk/4) {
> +			serial_icr_write(up, UART_TCR, 0x4);
> +		} else
> +			serial_icr_write(up, UART_TCR, 0);

baud can't be larger than port->uartclk/4 since you limited it above.

> +	}
> +	
>  	/*
>  	 * Oxford Semi 952 rev B workaround
>  	 */

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] 8250.c: Fix to make 16C950 UARTs work
  2005-09-09 10:18     ` Russell King
@ 2005-09-09 14:42       ` Mathias Adam
  2005-09-16 12:11       ` Mathias Adam
  1 sibling, 0 replies; 6+ messages in thread
From: Mathias Adam @ 2005-09-09 14:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: rmk+serial

Russell King wrote:
> On Fri, Sep 09, 2005 at 04:49:27AM +0200, Mathias Adam wrote:
> > +	if (up->port.type == PORT_16C950) {
> > +		unsigned int baud_base = port->uartclk/16;
> 
> baud_base appears unused.

you're right, it's not necessary anymore. (New patch below)

> > +		if (baud <= port->uartclk/16)
> > +			serial_icr_write(up, UART_TCR, 0);
> > +		else if (baud <= port->uartclk/8) {
> > +			serial_icr_write(up, UART_TCR, 0x8);
> > +		} else if (baud <= port->uartclk/4) {
> > +			serial_icr_write(up, UART_TCR, 0x4);
> > +		} else
> > +			serial_icr_write(up, UART_TCR, 0);
> 
> baud can't be larger than port->uartclk/4 since you limited it above.

Those lines come from 2.4.29's drivers/char/serial.c (>= line 1686). I
left in the last "else" to have some fallback if uart_get_baud_rate()
would change its behaviour to something else (i.e. does allow baud to
be larger than max_baud). However this would lead to an incorrect baud
rate to be set anyway (as the maximum baud rate of 16C950 is uartclk/4),
so one could simply set the "/4" mode for everything larger than uartclk/8.

Btw, if you look at lines 1700-1701 of original 2.6.13's 8250.c:

    baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
    quot = serial8250_get_divisor(port, baud);

baud is limited to uartclk/16 here, but serial8250_get_divisor() tests
it for being uartclk/4 or uartclk/8...
I'm afraid that max_baud thing has to become somewhat more general, or
do I miss something?

Hmm and I don't get the point in the calculation of "quot" - is that
formula correct in every case? I'll look into that a little bit now.

Mathias Adam

PS: I'm now subscribed to the list.


--- linux-2.6.13-org/drivers/serial/8250.c	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/8250.c	2005-09-09 15:33:23.000000000 +0200
@@ -1665,7 +1665,7 @@
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
-	unsigned int baud, quot;
+	unsigned int baud, quot, max_baud;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -1697,9 +1697,25 @@
 	/*
 	 * Ask the core to calculate the divisor for us.
 	 */
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+	baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
 	quot = serial8250_get_divisor(port, baud);
 
+	/* 
+	 * 16C950 supports additional prescaler ratios between 1:16 and 1:4
+	 * thus increasing max baud rate to uartclk/4. The following was taken
+	 * from kernel 2.4 by Mathias Adam <a2@adamis.de> to make the Socket
+	 * Bluetooth CF Card work under 2.6.13.
+	 */
+	if (up->port.type == PORT_16C950) {
+		if (baud <= port->uartclk/16)
+			serial_icr_write(up, UART_TCR, 0);
+		else if (baud <= port->uartclk/8) {
+			serial_icr_write(up, UART_TCR, 0x8);
+		} else
+			serial_icr_write(up, UART_TCR, 0x4);
+	}
+	
 	/*
 	 * Oxford Semi 952 rev B workaround
 	 */

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] 8250.c: Fix to make 16C950 UARTs work
  2005-09-09 10:18     ` Russell King
  2005-09-09 14:42       ` Mathias Adam
@ 2005-09-16 12:11       ` Mathias Adam
  1 sibling, 0 replies; 6+ messages in thread
From: Mathias Adam @ 2005-09-16 12:11 UTC (permalink / raw)
  To: Russell King; +Cc: linux-kernel

I've reworked the patch a little. Now it should enable both the 230400 and
the 460800 baud rates on any serial port which is using a 16C95x UART.
However as my 16C950 device is part of a Bluetooth dongle I couldn't
test the 460800 baud rate myself (I am able to set this rate with stty
though).
Please, could someone who owns such a UART try this out?
Any other comments?

Regards
Mathias



--- linux-2.6.13-org/drivers/serial/8250.c	2005-08-29 01:41:01.000000000 +0200
+++ linux-2.6.13/drivers/serial/8250.c	2005-09-16 12:18:14.000000000 +0200
@@ -7,6 +7,9 @@
  *
  *  Copyright (C) 2001 Russell King.
  *
+ *  2005/09/16: Enabled higher baud rates for 16C95x.
+ *		(Mathias Adam <a2@adamis.de>)
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -1652,6 +1655,14 @@
 	else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
 		 baud == (port->uartclk/8))
 		quot = 0x8002;
+	/*
+	 * For 16C950s UART_TCR is used in combination with divisor==1
+	 * to achieve baud rates up to baud_base*4.
+	 */
+	else if ((port->type == PORT_16C950) &&
+		 baud > (port->uartclk/16))
+		quot = 1;
+
 	else
 		quot = uart_get_divisor(port, baud);
 
@@ -1665,7 +1676,7 @@
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
-	unsigned int baud, quot;
+	unsigned int baud, quot, max_baud;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -1697,7 +1708,8 @@
 	/*
 	 * Ask the core to calculate the divisor for us.
 	 */
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+	baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
 	quot = serial8250_get_divisor(port, baud);
 
 	/*
@@ -1733,6 +1745,19 @@
 	 */
 	spin_lock_irqsave(&up->port.lock, flags);
 
+	/* 
+	 * 16C950 supports additional prescaler ratios between 1:16 and 1:4
+	 * thus increasing max baud rate to uartclk/4.
+	 */
+	if (up->port.type == PORT_16C950) {
+		if (baud == port->uartclk/4)
+			serial_icr_write(up, UART_TCR, 0x4);
+		else if (baud == port->uartclk/8)
+			serial_icr_write(up, UART_TCR, 0x8);
+		else
+			serial_icr_write(up, UART_TCR, 0);
+	}
+	
 	/*
 	 * Update the per-port timeout.
 	 */

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2005-09-16 12:10 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-09-09  1:31 [PATCH] 8250.c: Fix to make 16C950 UARTs work Mathias Adam
2005-09-09  1:58 ` Stefan Smietanowski
2005-09-09  2:49   ` Mathias Adam
2005-09-09 10:18     ` Russell King
2005-09-09 14:42       ` Mathias Adam
2005-09-16 12:11       ` Mathias Adam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox