Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 338415
b: refs/heads/master
c: 2dff8ad
h: refs/heads/master
i:
  338413: 6fa1391
  338411: df5b15d
  338407: 7fcf335
  338399: 771fefc
v: v3
  • Loading branch information
Gabor Juhos authored and Greg Kroah-Hartman committed Nov 16, 2012
1 parent fbc0fa0 commit 1b6cf9b
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 6 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5318609519800617323b5fdb17c1d4fe12c3d794
refs/heads/master: 2dff8ad92661b6c99e9ba8b5918e43b522551556
90 changes: 85 additions & 5 deletions trunk/drivers/tty/serial/ar933x_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,28 @@
#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;

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,
Expand Down Expand Up @@ -162,14 +172,65 @@ static void ar933x_uart_enable_ms(struct uart_port *port)
{
}

/*
* 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)
{
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;
Expand All @@ -191,15 +252,19 @@ static void ar933x_uart_set_termios(struct uart_port *port,
/* 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
* interrupts disabled.
*/
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);

Expand All @@ -210,7 +275,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
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);
Expand All @@ -219,6 +284,11 @@ static void ar933x_uart_set_termios(struct uart_port *port,
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))
Expand Down Expand Up @@ -401,14 +471,17 @@ static void ar933x_uart_config_port(struct uart_port *port, int flags)
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;

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;
Expand Down Expand Up @@ -561,6 +634,7 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev)
struct uart_port *port;
struct resource *mem_res;
struct resource *irq_res;
unsigned int baud;
int id;
int ret;

Expand Down Expand Up @@ -611,6 +685,12 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev)
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);
Expand Down

0 comments on commit 1b6cf9b

Please sign in to comment.