diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-05-07 10:54:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-05-07 10:54:32 -0700 |
commit | 2c66fa7e6be6bdb88587ac13ac1de080d5be4f95 (patch) | |
tree | 6c11f9580c6d9c6e864aad015cf87a07741bcfb2 /drivers/serial/samsung.c | |
parent | 60db402780ec257b287de591d65157575952bb4a (diff) | |
parent | ae51e609843f7d0aaeb1c2ad9f89d252a4899885 (diff) |
Merge master.kernel.org:/home/rmk/linux-2.6-arm
* master.kernel.org:/home/rmk/linux-2.6-arm:
[ARM] 5507/1: support R_ARM_MOVW_ABS_NC and MOVT_ABS relocation types
[ARM] 5506/1: davinci: DMA_32BIT_MASK --> DMA_BIT_MASK(32)
i.MX31: Disable CPU_32v6K in mx3_defconfig.
mx3fb: Fix compilation with CONFIG_PM
mx27ads: move PBC mapping out of vmalloc space
MXC: remove BUG_ON in interrupt handler
mx31: remove mx31moboard_defconfig
ARM: ARCH_MXC should select HAVE_CLK
mxc : BUG in imx_dma_request
mxc : Clean up properly when imx_dma_free() used without imx_dma_disable()
[ARM] mv78xx0: update defconfig
[ARM] orion5x: update defconfig
[ARM] Kirkwood: update defconfig
[ARM] Kconfig typo fix: "PXA930" -> "CPU_PXA930".
[ARM] S3C2412: Add missing cache flush in suspend code
[ARM] S3C: Add UDIVSLOT support for newer UARTS
[ARM] S3C64XX: Add S3C64XX_PA_IIS{0,1} to <mach/map.h>
Diffstat (limited to 'drivers/serial/samsung.c')
-rw-r--r-- | drivers/serial/samsung.c | 61 |
1 files changed, 58 insertions, 3 deletions
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index e06686ae858..93b5d75db12 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -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; }; @@ -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); @@ -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; @@ -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) @@ -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. @@ -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)) { @@ -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"); @@ -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), |