From 932f54688f83ab4a7532614042398dd46722dcd2 Mon Sep 17 00:00:00 2001 From: Alexey Galakhov Date: Mon, 14 May 2012 15:38:05 +0600 Subject: Support most Samsung SoCs in S3C serial driver Ok, I assume this should go into separate series. It fits both S5PV210 and S3C6410. This adds support for S3C and S5P architectures (all of my knowledge) to the serial driver. Since the only difference between them is in clock handling, this is moved to an arch-dependent separate function. Most modern architectures should define S3C_UART_HAS_UBRDIVSLOT and S3C_UART_HAS_UINTM. This adds support for most Signed-off-by: Alexey Galakhov Signed-off-by: Juergen Beisewrt Signed-off-by: Sascha Hauer --- drivers/serial/Kconfig | 2 +- drivers/serial/serial_s3c.c | 55 +++++++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 186b59638f..a9383da9d7 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -80,7 +80,7 @@ config DRIVER_SERIAL_PL010 config DRIVER_SERIAL_S3C bool "Samsung S3C serial driver" - depends on ARCH_S3C24xx + depends on ARCH_SAMSUNG default y help Say Y here if you want to use the CONS on a Samsung S3C CPU diff --git a/drivers/serial/serial_s3c.c b/drivers/serial/serial_s3c.c index 2bdc1df696..7a9b355704 100644 --- a/drivers/serial/serial_s3c.c +++ b/drivers/serial/serial_s3c.c @@ -40,6 +40,17 @@ #define UTXH 0x20 /* transmitt */ #define URXH 0x24 /* receive */ #define UBRDIV 0x28 /* baudrate generator */ +#ifdef S3C_UART_HAS_UBRDIVSLOT +# define UBRDIVSLOT 0x2c /* baudrate slot generator */ +#endif +#ifdef S3C_UART_HAS_UINTM +# define UINTM 0x38 /* interrupt mask register */ +#endif + +#ifndef S3C_UART_CLKSEL +/* Use pclk */ +# define S3C_UART_CLKSEL 0 +#endif struct s3c_uart { void __iomem *regs; @@ -51,26 +62,32 @@ struct s3c_uart { static unsigned s3c_get_arch_uart_input_clock(void __iomem *base) { unsigned reg = readw(base + UCON); - - switch (reg & 0xc00) { - case 0x000: - case 0x800: - return s3c_get_pclk(); - case 0x400: - break; /* TODO UEXTCLK */ - case 0xc00: - break; /* TODO FCLK/n */ - } - - return 0; /* not nice, but we can't emit an error message! */ + reg = (reg >> 10) & 0x3; + return s3c_get_uart_clk(reg); } +#ifdef S3C_UART_HAS_UBRDIVSLOT +/* + * This table takes the fractional value of the baud divisor and gives + * the recommended setting for the UDIVSLOT register. Refer the datasheet + * for further details + */ +static const uint16_t udivslot_table[] __maybe_unused = { + 0x0000, 0x0080, 0x0808, 0x0888, 0x2222, 0x4924, 0x4A52, 0x54AA, + 0x5555, 0xD555, 0xD5D5, 0xDDD5, 0xDDDD, 0xDFDD, 0xDFDF, 0xFFDF, +}; +#endif + static int s3c_serial_setbaudrate(struct console_device *cdev, int baudrate) { struct s3c_uart *priv = to_s3c_uart(cdev); void __iomem *base = priv->regs; unsigned val; +#ifdef S3C_UART_HAS_UBRDIVSLOT + val = s3c_get_arch_uart_input_clock(base) / baudrate; + writew(udivslot_table[val & 15], base + UBRDIVSLOT); +#endif val = s3c_get_arch_uart_input_clock(base) / (16 * baudrate) - 1; writew(val, base + UBRDIV); @@ -88,11 +105,15 @@ static int s3c_serial_init_port(struct console_device *cdev) /* Normal,No parity,1 stop,8 bit */ writeb(0x03, base + ULCON); - /* - * tx=level,rx=edge,disable timeout int.,enable rx error int., - * normal,interrupt or polling - */ - writew(0x0245, base + UCON); + + /* tx=level,rx=edge,disable timeout int.,enable rx error int., + * normal, interrupt or polling, no pre-divider */ + writew(0x0245 | ((S3C_UART_CLKSEL) << 10), base + UCON); + +#ifdef S3C_UART_HAS_UINTM + /* 'interrupt or polling mode' for both directions */ + writeb(0xf, base + UINTM); +#endif #ifdef CONFIG_DRIVER_SERIAL_S3C_AUTOSYNC writeb(0x10, base + UMCON); /* enable auto flow control */ -- cgit v1.2.3