Commit d0e462fc authored by David Woodhouse's avatar David Woodhouse

Allow uart drivers to calculate divisors differently.

In order to deal with the weird and wonderful ways of obtaining a uart clock
which a lot of the high-speed hacks for 16550-based chips seem to have, we need
to make uart_update_timeout() and uart_get_divisor() take the desired baud rate
as their arguments, instead of termios or divisor as before. The main reason for the
change to uart_get_divisor(), requiring a call to uart_get_baud_rate() before it, is
so that the drivers actually _have_ the baud rate in order that they can pass it to
uart_update_timeout().
parent 15f1049d
...@@ -237,7 +237,7 @@ serial21285_set_termios(struct uart_port *port, struct termios *termios, ...@@ -237,7 +237,7 @@ serial21285_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old) struct termios *old)
{ {
unsigned long flags; unsigned long flags;
unsigned int quot, h_lcr; unsigned int baud, quot, h_lcr;
/* /*
* We don't support modem control lines. * We don't support modem control lines.
...@@ -253,7 +253,8 @@ serial21285_set_termios(struct uart_port *port, struct termios *termios, ...@@ -253,7 +253,8 @@ serial21285_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
switch (termios->c_cflag & CSIZE) { switch (termios->c_cflag & CSIZE) {
case CS5: case CS5:
...@@ -286,7 +287,7 @@ serial21285_set_termios(struct uart_port *port, struct termios *termios, ...@@ -286,7 +287,7 @@ serial21285_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
/* /*
* Which character status flags are we interested in? * Which character status flags are we interested in?
......
...@@ -1325,7 +1325,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, ...@@ -1325,7 +1325,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
struct uart_8250_port *up = (struct uart_8250_port *)port; struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char cval, fcr = 0; unsigned char cval, fcr = 0;
unsigned long flags; unsigned long flags;
unsigned int quot; unsigned int baud, quot;
switch (termios->c_cflag & CSIZE) { switch (termios->c_cflag & CSIZE) {
case CS5: case CS5:
...@@ -1357,7 +1357,8 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, ...@@ -1357,7 +1357,8 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
/* /*
* Work around a bug in the Oxford Semiconductor 952 rev B * Work around a bug in the Oxford Semiconductor 952 rev B
...@@ -1369,7 +1370,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, ...@@ -1369,7 +1370,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
quot ++; quot ++;
if (uart_config[up->port.type].flags & UART_USE_FIFO) { if (uart_config[up->port.type].flags & UART_USE_FIFO) {
if ((up->port.uartclk / quot) < (2400 * 16)) if (baud < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
#ifdef CONFIG_SERIAL_8250_RSA #ifdef CONFIG_SERIAL_8250_RSA
else if (up->port.type == PORT_RSA) else if (up->port.type == PORT_RSA)
...@@ -1390,7 +1391,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, ...@@ -1390,7 +1391,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (termios->c_iflag & INPCK) if (termios->c_iflag & INPCK)
......
...@@ -408,12 +408,13 @@ ambauart_set_termios(struct uart_port *port, struct termios *termios, ...@@ -408,12 +408,13 @@ ambauart_set_termios(struct uart_port *port, struct termios *termios,
{ {
unsigned int lcr_h, old_cr; unsigned int lcr_h, old_cr;
unsigned long flags; unsigned long flags;
unsigned int quot; unsigned int baud, quot;
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
switch (termios->c_cflag & CSIZE) { switch (termios->c_cflag & CSIZE) {
case CS5: case CS5:
...@@ -444,7 +445,7 @@ ambauart_set_termios(struct uart_port *port, struct termios *termios, ...@@ -444,7 +445,7 @@ ambauart_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = AMBA_UARTRSR_OE; port->read_status_mask = AMBA_UARTRSR_OE;
if (termios->c_iflag & INPCK) if (termios->c_iflag & INPCK)
......
...@@ -287,7 +287,7 @@ anakin_set_termios(struct uart_port *port, struct termios *termios, ...@@ -287,7 +287,7 @@ anakin_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old) struct termios *old)
{ {
unsigned long flags; unsigned long flags;
unsigned int quot; unsigned int baud, quot;
/* /*
* We don't support parity, stop bits, or anything other * We don't support parity, stop bits, or anything other
...@@ -304,11 +304,12 @@ anakin_set_termios(struct uart_port *port, struct termios *termios, ...@@ -304,11 +304,12 @@ anakin_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
while (!(anakin_in(port, 0x10) & TXEMPTY)) while (!(anakin_in(port, 0x10) & TXEMPTY))
barrier(); barrier();
......
...@@ -320,7 +320,7 @@ static void ...@@ -320,7 +320,7 @@ static void
clps711xuart_set_termios(struct uart_port *port, struct termios *termios, clps711xuart_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old) struct termios *old)
{ {
unsigned int ubrlcr, quot; unsigned int ubrlcr, baud, quot;
unsigned long flags; unsigned long flags;
/* /*
...@@ -331,7 +331,8 @@ clps711xuart_set_termios(struct uart_port *port, struct termios *termios, ...@@ -331,7 +331,8 @@ clps711xuart_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
switch (termios->c_cflag & CSIZE) { switch (termios->c_cflag & CSIZE) {
case CS5: case CS5:
...@@ -362,7 +363,7 @@ clps711xuart_set_termios(struct uart_port *port, struct termios *termios, ...@@ -362,7 +363,7 @@ clps711xuart_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = UARTDR_OVERR; port->read_status_mask = UARTDR_OVERR;
if (termios->c_iflag & INPCK) if (termios->c_iflag & INPCK)
......
...@@ -271,7 +271,7 @@ static void uart_shutdown(struct uart_info *info) ...@@ -271,7 +271,7 @@ static void uart_shutdown(struct uart_info *info)
*/ */
void void
uart_update_timeout(struct uart_port *port, unsigned int cflag, uart_update_timeout(struct uart_port *port, unsigned int cflag,
unsigned int quot) unsigned int baud)
{ {
unsigned int bits; unsigned int bits;
...@@ -305,7 +305,7 @@ uart_update_timeout(struct uart_port *port, unsigned int cflag, ...@@ -305,7 +305,7 @@ uart_update_timeout(struct uart_port *port, unsigned int cflag,
* Figure the timeout to send the above number of bits. * Figure the timeout to send the above number of bits.
* Add .02 seconds of slop * Add .02 seconds of slop
*/ */
port->timeout = (HZ * bits) / (port->uartclk / (16 * quot)) + HZ/50; port->timeout = (HZ * bits) / baud + HZ/50;
} }
EXPORT_SYMBOL(uart_update_timeout); EXPORT_SYMBOL(uart_update_timeout);
...@@ -321,6 +321,12 @@ EXPORT_SYMBOL(uart_update_timeout); ...@@ -321,6 +321,12 @@ EXPORT_SYMBOL(uart_update_timeout);
* Decode the termios structure into a numeric baud rate, * Decode the termios structure into a numeric baud rate,
* taking account of the magic 38400 baud rate (with spd_* * taking account of the magic 38400 baud rate (with spd_*
* flags), and mapping the %B0 rate to 9600 baud. * flags), and mapping the %B0 rate to 9600 baud.
*
* If the new baud rate is invalid, try the old termios setting.
* If it's still invalid, we try 9600 baud.
*
* Update the @termios structure to reflect the baud rate
* we're actually going to be using.
*/ */
unsigned int unsigned int
uart_get_baud_rate(struct uart_port *port, struct termios *termios, uart_get_baud_rate(struct uart_port *port, struct termios *termios,
...@@ -383,26 +389,14 @@ EXPORT_SYMBOL(uart_get_baud_rate); ...@@ -383,26 +389,14 @@ EXPORT_SYMBOL(uart_get_baud_rate);
/** /**
* uart_get_divisor - return uart clock divisor * uart_get_divisor - return uart clock divisor
* @port: uart_port structure describing the port. * @port: uart_port structure describing the port.
* @termios: desired termios settings * @baud: desired baud rate
* @old_termios: the original port settings, or NULL
*
* Calculate the uart clock divisor for the port. If the
* divisor is invalid, try the old termios setting. If
* the divisor is still invalid, we try 9600 baud.
* *
* Update the @termios structure to reflect the baud rate * Calculate the uart clock divisor for the port.
* we're actually going to be using.
*
* If 9600 baud fails, we return a zero divisor.
*/ */
unsigned int unsigned int
uart_get_divisor(struct uart_port *port, struct termios *termios, uart_get_divisor(struct uart_port *port, unsigned int baud)
struct termios *old_termios)
{ {
unsigned int quot, baud, max = port->uartclk / 16; unsigned int quot;
/* Determine divisor based on baud rate */
baud = uart_get_baud_rate(port, termios, old_termios, 0, max);
/* /*
* Old custom speed handling. * Old custom speed handling.
......
...@@ -441,7 +441,7 @@ sa1100_set_termios(struct uart_port *port, struct termios *termios, ...@@ -441,7 +441,7 @@ sa1100_set_termios(struct uart_port *port, struct termios *termios,
{ {
struct sa1100_port *sport = (struct sa1100_port *)port; struct sa1100_port *sport = (struct sa1100_port *)port;
unsigned long flags; unsigned long flags;
unsigned int utcr0, old_utcr3, quot; unsigned int utcr0, old_utcr3, baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
/* /*
...@@ -470,7 +470,8 @@ sa1100_set_termios(struct uart_port *port, struct termios *termios, ...@@ -470,7 +470,8 @@ sa1100_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
spin_lock_irqsave(&sport->port.lock, flags); spin_lock_irqsave(&sport->port.lock, flags);
...@@ -507,7 +508,7 @@ sa1100_set_termios(struct uart_port *port, struct termios *termios, ...@@ -507,7 +508,7 @@ sa1100_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
/* /*
* disable interrupts and drain transmitter * disable interrupts and drain transmitter
......
...@@ -853,7 +853,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, ...@@ -853,7 +853,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, cflag, quot); uart_update_timeout(port, cflag, (port->uartclk / (16 * quot)));
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (iflag & INPCK) if (iflag & INPCK)
...@@ -920,12 +920,13 @@ static void ...@@ -920,12 +920,13 @@ static void
sunsu_set_termios(struct uart_port *port, struct termios *termios, sunsu_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old) struct termios *old)
{ {
unsigned int quot; unsigned int baud, quot;
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
sunsu_change_speed(port, termios->c_cflag, termios->c_iflag, quot); sunsu_change_speed(port, termios->c_cflag, termios->c_iflag, quot);
} }
......
...@@ -317,7 +317,7 @@ static void ...@@ -317,7 +317,7 @@ static void
uart00_set_termios(struct uart_port *port, struct termios *termios, uart00_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old) struct termios *old)
{ {
unsigned int uart_mc, old_ies, quot; unsigned int uart_mc, old_ies, baud, quot;
unsigned long flags; unsigned long flags;
/* /*
...@@ -328,7 +328,8 @@ uart00_set_termios(struct uart_port *port, struct termios *termios, ...@@ -328,7 +328,8 @@ uart00_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
quot = uart_get_divisor(port, termios, old); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
/* byte size and parity */ /* byte size and parity */
switch (termios->c_cflag & CSIZE) { switch (termios->c_cflag & CSIZE) {
...@@ -358,7 +359,7 @@ uart00_set_termios(struct uart_port *port, struct termios *termios, ...@@ -358,7 +359,7 @@ uart00_set_termios(struct uart_port *port, struct termios *termios,
/* /*
* Update the per-port timeout. * Update the per-port timeout.
*/ */
uart_update_timeout(port, termios->c_cflag, quot); uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = UART_RDS_OE_MSK; port->read_status_mask = UART_RDS_OE_MSK;
if (termios->c_iflag & INPCK) if (termios->c_iflag & INPCK)
......
...@@ -279,12 +279,11 @@ void uart_write_wakeup(struct uart_port *port); ...@@ -279,12 +279,11 @@ void uart_write_wakeup(struct uart_port *port);
* Baud rate helpers. * Baud rate helpers.
*/ */
void uart_update_timeout(struct uart_port *port, unsigned int cflag, void uart_update_timeout(struct uart_port *port, unsigned int cflag,
unsigned int quot); unsigned int baud);
unsigned int uart_get_baud_rate(struct uart_port *port, struct termios *termios, unsigned int uart_get_baud_rate(struct uart_port *port, struct termios *termios,
struct termios *old, unsigned int min, struct termios *old, unsigned int min,
unsigned int max); unsigned int max);
unsigned int uart_get_divisor(struct uart_port *port, struct termios *termios, unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud);
struct termios *old_termios);
/* /*
* Console helpers. * Console helpers.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment