Skip to content

Commit

Permalink
[ARM] S3C: Add UDIVSLOT support for newer UARTS
Browse files Browse the repository at this point in the history
Add support for the UDIVSLOT register on the newer UART blocks
which gives the capability of 1/16ths adjustment to the baud rate.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
  • Loading branch information
Ben Dooks committed May 1, 2009
1 parent 5ef316f commit 090f848
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
1 change: 1 addition & 0 deletions drivers/serial/s3c6400.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ static struct s3c24xx_uart_info s3c6400_uart_inf = {
.name = "Samsung S3C6400 UART",
.type = PORT_S3C6400,
.fifosize = 64,
.has_divslot = 1,
.rx_fifomask = S3C2440_UFSTAT_RXMASK,
.rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
.rx_fifofull = S3C2440_UFSTAT_RXFULL,
Expand Down
61 changes: 58 additions & 3 deletions drivers/serial/samsung.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
struct baud_calc {
struct s3c24xx_uart_clksrc *clksrc;
unsigned int calc;
unsigned int divslot;
unsigned int quot;
struct clk *src;
};
Expand All @@ -517,6 +518,7 @@ static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
struct s3c24xx_uart_clksrc *clksrc,
unsigned int baud)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
unsigned long rate;

calc->src = clk_get(port->dev, clksrc->name);
Expand All @@ -527,8 +529,24 @@ static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
rate /= clksrc->divisor;

calc->clksrc = clksrc;
calc->quot = (rate + (8 * baud)) / (16 * baud);
calc->calc = (rate / (calc->quot * 16));

if (ourport->info->has_divslot) {
unsigned long div = rate / baud;

/* The UDIVSLOT register on the newer UARTs allows us to
* get a divisor adjustment of 1/16th on the baud clock.
*
* We don't keep the UDIVSLOT value (the 16ths we calculated
* by not multiplying the baud by 16) as it is easy enough
* to recalculate.
*/

calc->quot = div / 16;
calc->calc = rate / div;
} else {
calc->quot = (rate + (8 * baud)) / (16 * baud);
calc->calc = (rate / (calc->quot * 16));
}

calc->quot--;
return 1;
Expand Down Expand Up @@ -611,6 +629,30 @@ static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
return best->quot;
}

/* udivslot_table[]
*
* This table takes the fractional value of the baud divisor and gives
* the recommended setting for the UDIVSLOT register.
*/
static u16 udivslot_table[16] = {
[0] = 0x0000,
[1] = 0x0080,
[2] = 0x0808,
[3] = 0x0888,
[4] = 0x2222,
[5] = 0x4924,
[6] = 0x4A52,
[7] = 0x54AA,
[8] = 0x5555,
[9] = 0xD555,
[10] = 0xD5D5,
[11] = 0xDDD5,
[12] = 0xDDDD,
[13] = 0xDFDD,
[14] = 0xDFDF,
[15] = 0xFFDF,
};

static void s3c24xx_serial_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
Expand All @@ -623,6 +665,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
unsigned int baud, quot;
unsigned int ulcon;
unsigned int umcon;
unsigned int udivslot = 0;

/*
* We don't support modem control lines.
Expand All @@ -644,6 +687,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
/* check to see if we need to change clock source */

if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
dbg("selecting clock %p\n", clk);
s3c24xx_serial_setsource(port, clksrc);

if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
Expand All @@ -658,6 +702,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}

if (ourport->info->has_divslot) {
unsigned int div = ourport->baudclk_rate / baud;

udivslot = udivslot_table[div & 15];
dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
}

switch (termios->c_cflag & CSIZE) {
case CS5:
dbg("config: 5bits/char\n");
Expand Down Expand Up @@ -697,12 +748,16 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,

spin_lock_irqsave(&port->lock, flags);

dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
ulcon, quot, udivslot);

wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot);
wr_regl(port, S3C2410_UMCON, umcon);

if (ourport->info->has_divslot)
wr_regl(port, S3C2443_DIVSLOT, udivslot);

dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
rd_regl(port, S3C2410_ULCON),
rd_regl(port, S3C2410_UCON),
Expand Down
4 changes: 4 additions & 0 deletions drivers/serial/samsung.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ struct s3c24xx_uart_info {
unsigned long tx_fifoshift;
unsigned long tx_fifofull;

/* uart port features */

unsigned int has_divslot:1;

/* clock source control */

int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
Expand Down

0 comments on commit 090f848

Please sign in to comment.