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 Cc: David Woodhouse Signed-off-by: Andrew Morton --- 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 --- devel/drivers/serial/8250.c~serial-add-support-for-non-standard-xtals-to-16c950-driver 2005-09-12 03:34:57.000000000 -0700 +++ devel-akpm/drivers/serial/8250.c 2005-09-12 03:34:57.000000000 -0700 @@ -1653,24 +1653,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 @@ -1680,7 +1714,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: @@ -1712,8 +1746,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); /* * Oxford Semi 952 rev B workaround @@ -1817,6 +1856,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. */ _ 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171