diff options
| -rw-r--r-- | target/linux/ar71xx/patches-3.3/211-ar933x_uart-improve-serial-clock-calculation.patch | 181 | 
1 files changed, 181 insertions, 0 deletions
| diff --git a/target/linux/ar71xx/patches-3.3/211-ar933x_uart-improve-serial-clock-calculation.patch b/target/linux/ar71xx/patches-3.3/211-ar933x_uart-improve-serial-clock-calculation.patch new file mode 100644 index 000000000..510b75ed0 --- /dev/null +++ b/target/linux/ar71xx/patches-3.3/211-ar933x_uart-improve-serial-clock-calculation.patch @@ -0,0 +1,181 @@ +--- a/drivers/tty/serial/ar933x_uart.c ++++ b/drivers/tty/serial/ar933x_uart.c +@@ -25,11 +25,19 @@ + #include <linux/io.h> + #include <linux/irq.h> +  ++#include <asm/div64.h> ++ + #include <asm/mach-ath79/ar933x_uart.h> + #include <asm/mach-ath79/ar933x_uart_platform.h> +  + #define DRIVER_NAME "ar933x-uart" +  ++#define AR933X_UART_MAX_SCALE	0xff ++#define AR933X_UART_MAX_STEP	0xffff ++ ++#define AR933X_UART_MIN_BAUD	300 ++#define AR933X_UART_MAX_BAUD	3000000 ++ + #define AR933X_DUMMY_STATUS_RD	0x01 +  + static struct uart_driver ar933x_uart_driver; +@@ -37,6 +45,8 @@ static struct uart_driver ar933x_uart_dr + struct ar933x_uart_port { + 	struct uart_port	port; + 	unsigned int		ier;	/* shadow Interrupt Enable Register */ ++	unsigned int		min_baud; ++	unsigned int		max_baud; + }; +  + static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up, +@@ -162,6 +172,57 @@ static void ar933x_uart_enable_ms(struct + { + } +  ++/* ++ * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17)) ++ */ ++static unsigned long ar933x_uart_get_baud(unsigned int clk, ++					  unsigned int scale, ++					  unsigned int step) ++{ ++	u64 t; ++	u32 div; ++ ++	div = (2 << 16) * (scale + 1); ++	t = clk; ++	t *= step; ++	t += (div / 2); ++	do_div(t, div); ++ ++	return t; ++} ++ ++static void ar933x_uart_get_scale_step(unsigned int clk, ++				       unsigned int baud, ++				       unsigned int *scale, ++				       unsigned int *step) ++{ ++	unsigned int tscale; ++	long min_diff; ++ ++	*scale = 0; ++	*step = 0; ++ ++	min_diff = baud; ++	for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) { ++		u64 tstep; ++		int diff; ++ ++		tstep = baud * (tscale + 1); ++		tstep *= (2 << 16); ++		do_div(tstep, clk); ++ ++		if (tstep > AR933X_UART_MAX_STEP) ++			break; ++ ++		diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud); ++		if (diff < min_diff) { ++			min_diff = diff; ++			*scale = tscale; ++			*step = tstep; ++		} ++	} ++} ++ + static void ar933x_uart_set_termios(struct uart_port *port, + 				    struct ktermios *new, + 				    struct ktermios *old) +@@ -169,7 +230,7 @@ static void ar933x_uart_set_termios(stru + 	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; + 	unsigned int cs; + 	unsigned long flags; +-	unsigned int baud, scale; ++	unsigned int baud, scale, step; +  + 	/* Only CS8 is supported */ + 	new->c_cflag &= ~CSIZE; +@@ -191,8 +252,8 @@ static void ar933x_uart_set_termios(stru + 	/* Mark/space parity is not supported */ + 	new->c_cflag &= ~CMSPAR; +  +-	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); +-	scale = (port->uartclk / (16 * baud)) - 1; ++	baud = uart_get_baud_rate(port, new, old, up->min_baud, up->max_baud); ++	ar933x_uart_get_scale_step(port->uartclk, baud, &scale, &step); +  + 	/* + 	 * Ok, we're now changing the port state. Do it with +@@ -200,6 +261,10 @@ static void ar933x_uart_set_termios(stru + 	 */ + 	spin_lock_irqsave(&up->port.lock, flags); +  ++	/* disable the UART */ ++	ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, ++		      AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S); ++ + 	/* Update the per-port timeout. */ + 	uart_update_timeout(port, new->c_cflag, baud); +  +@@ -210,7 +275,7 @@ static void ar933x_uart_set_termios(stru + 		up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD; +  + 	ar933x_uart_write(up, AR933X_UART_CLOCK_REG, +-			  scale << AR933X_UART_CLOCK_SCALE_S | 8192); ++			  scale << AR933X_UART_CLOCK_SCALE_S | step); +  + 	/* setup configuration register */ + 	ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs); +@@ -219,6 +284,11 @@ static void ar933x_uart_set_termios(stru + 	ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, + 			    AR933X_UART_CS_HOST_INT_EN); +  ++	/* reenable the UART */ ++	ar933x_uart_rmw(up, AR933X_UART_CS_REG, ++		        AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, ++		        AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S); ++ + 	spin_unlock_irqrestore(&up->port.lock, flags); +  + 	if (tty_termios_baud_rate(new)) +@@ -401,6 +471,8 @@ static void ar933x_uart_config_port(stru + static int ar933x_uart_verify_port(struct uart_port *port, + 				   struct serial_struct *ser) + { ++	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port; ++ + 	if (ser->type != PORT_UNKNOWN && + 	    ser->type != PORT_AR933X) + 		return -EINVAL; +@@ -408,7 +480,8 @@ static int ar933x_uart_verify_port(struc + 	if (ser->irq < 0 || ser->irq >= NR_IRQS) + 		return -EINVAL; +  +-	if (ser->baud_base < 28800) ++	if (ser->baud_base < up->min_baud || ++	    ser->baud_base > up->max_baud) + 		return -EINVAL; +  + 	return 0; +@@ -561,6 +634,7 @@ static int __devinit ar933x_uart_probe(s + 	struct uart_port *port; + 	struct resource *mem_res; + 	struct resource *irq_res; ++	unsigned int baud; + 	int id; + 	int ret; +  +@@ -611,6 +685,12 @@ static int __devinit ar933x_uart_probe(s + 	port->fifosize = AR933X_UART_FIFO_SIZE; + 	port->ops = &ar933x_uart_ops; +  ++	baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1); ++	up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD); ++ ++	baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP); ++	up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD); ++ + 	ar933x_uart_add_console_port(up); +  + 	ret = uart_add_one_port(&ar933x_uart_driver, &up->port); | 
