From: Petr Vandrovec Patch below adds support for using different prescaler than 16 for 16c950 chips. This is needed for using Fujitsu-Siemens Connect2Air compact-flash card, which comes (apparently) with 806kHz clocks, and so you have to program prescaler for division by 7, and DLAB to 1, to get 115200Bd. To get card properly running you also have to add lines below to /etc/pcmcia/serial.opts so kernel knows that base speed is not 115200 but 50400 (50400 * 16 = 806400; 806400 / 7 = 115200). As I've found no code specifying baud_rate in serial_cs, I assume that specifying it in serial.opts is right way to do this type of things. Patch also fixes problem that for UPF_MAGIC_MULTIPLIER maximum possible baud rate passed to uart code was uartclk / 16 while correct value for these devices (and for 16c950) is uartclk / 4. Patch also fixes problem that for UPF_MAGIC_MULTIPLIER devices with baud_rate 19200 or 9600 spd_cust did not work correctly. Not that such devices exist, but we should not ignore spd_cust, user probably knows why he asked for spd_cust. serial.opts: case "$MANFID-$FUNCID-$PRODID_1-$PRODID_2-$PRODID_3-$PRODID_4" in '0279,950b-2-GPRS Modem---') SERIAL_OPTS="baud_base 50400" ;; esac Signed-off-by: Andrew Morton --- 25-akpm/drivers/serial/8250.c | 82 ++++++++++++++++++++++++++++++++---------- 1 files changed, 64 insertions(+), 18 deletions(-) diff -puN drivers/serial/8250.c~serial-add-support-for-non-standard-xtals-to-16c950-driver drivers/serial/8250.c --- 25/drivers/serial/8250.c~serial-add-support-for-non-standard-xtals-to-16c950-driver 2004-11-17 20:47:02.561846672 -0800 +++ 25-akpm/drivers/serial/8250.c 2004-11-17 20:47:02.566845912 -0800 @@ -1544,24 +1544,58 @@ static void serial8250_shutdown(struct u serial_unlink_irq_chain(up); } -static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) +static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud, + unsigned int *prescaler) { - unsigned int quot; - - /* - * Handle magic divisors for baud rates above baud_base on - * SMSC SuperIO chips. + /* + * Use special handling only if user did not supply its own divider. + * spd_cust is defined in terms of baud_base, so always use default + * prescaler when spd_cust is requested. */ - if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/4)) - quot = 0x8001; - else if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/8)) - quot = 0x8002; - else - quot = uart_get_divisor(port, baud); - return quot; + *prescaler = 16; + if (baud != 38400 || (port->flags & UPF_SPD_MASK) != UPF_SPD_CUST) { + unsigned int quot = port->uartclk / baud; + + /* + * Handle magic divisors for baud rates above baud_base on + * SMSC SuperIO chips. + */ + if (port->flags & UPF_MAGIC_MULTIPLIER) { + if (quot == 4) { + return 0x8001; + } else if (quot == 8) { + return 0x8002; + } + } + if (port->type == PORT_16C950) { + /* + * This computes TCR value (4 to 16), not CPR value (which can + * be between 1.000 and 31.875) - chip I have uses XTAL of + * 806400Hz, and so a division by 7 is required to get 115200Bd. + * I'm leaving CPR disabled for now, until someone will + * hit even more exotic XTAL (it is needed to get 500kbps + * or 1000kbps from 18.432MHz XTAL, but I have no device + * which would benefit from doing that). + * + * If we can use divide by 16, use it. Otherwise look for + * better prescaler, from 15 to 4. If quotient cannot + * be divided by any integer value between 4 and 15, use 4. + */ + if (quot & 0x0F) { + unsigned int div; + + for (div = 15; div > 4; div--) { + if (quot % div == 0) { + break; + } + } + *prescaler = div; + return quot / div; + } + } + } + return uart_get_divisor(port, baud); } static void @@ -1571,7 +1605,7 @@ serial8250_set_termios(struct uart_port 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, prescaler; switch (termios->c_cflag & CSIZE) { case CS5: @@ -1603,8 +1637,13 @@ serial8250_set_termios(struct uart_port /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = serial8250_get_divisor(port, baud); + + if (port->type == PORT_16C950 || (port->flags & UPF_MAGIC_MULTIPLIER)) { + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); + } else { + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + } + quot = serial8250_get_divisor(port, baud, &prescaler); /* * Work around a bug in the Oxford Semiconductor 952 rev B @@ -1711,6 +1750,13 @@ serial8250_set_termios(struct uart_port serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ /* + * Program prescaler for 16C950 chips. + */ + if (up->port.type == PORT_16C950) { + serial_icr_write(up, UART_TCR, prescaler == 16 ? 0 : prescaler); + } + + /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR * is written without DLAB set, this mode will be disabled. */ _