diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-12-31 04:36:49 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-12-31 04:36:49 +0300 |
commit | 14a3c4ab0e58d143c7928c9eb2f2610205e13bf2 (patch) | |
tree | 885992999d7a1a2fd3586efcf32ebcbcbc3a72aa /drivers/serial | |
parent | 1af237a099a3b8ff56aa384f605c6a68af7bf288 (diff) | |
parent | 47992cbdaef2f18a47871b2ed01ad27f568c8b73 (diff) | |
download | linux-14a3c4ab0e58d143c7928c9eb2f2610205e13bf2.tar.xz |
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (407 commits)
[ARM] pxafb: add support for overlay1 and overlay2 as framebuffer devices
[ARM] pxafb: cleanup of the timing checking code
[ARM] pxafb: cleanup of the color format manipulation code
[ARM] pxafb: add palette format support for LCCR4_PAL_FOR_3
[ARM] pxafb: add support for FBIOPAN_DISPLAY by dma braching
[ARM] pxafb: allow pxafb_set_par() to start from arbitrary yoffset
[ARM] pxafb: allow video memory size to be configurable
[ARM] pxa: add document on the MFP design and how to use it
[ARM] sa1100_wdt: don't assume CLOCK_TICK_RATE to be a constant
[ARM] rtc-sa1100: don't assume CLOCK_TICK_RATE to be a constant
[ARM] pxa/tavorevb: update board support (smartpanel LCD + keypad)
[ARM] pxa: Update eseries defconfig
[ARM] 5352/1: add w90p910-plat config file
[ARM] s3c: S3C options should depend on PLAT_S3C
[ARM] mv78xx0: implement GPIO and GPIO interrupt support
[ARM] Kirkwood: implement GPIO and GPIO interrupt support
[ARM] Orion: share GPIO IRQ handling code
[ARM] Orion: share GPIO handling code
[ARM] s3c: define __io using the typesafe version
[ARM] S3C64XX: Ensure CPU_V6 is selected
...
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/Kconfig | 25 | ||||
-rw-r--r-- | drivers/serial/Makefile | 2 | ||||
-rw-r--r-- | drivers/serial/amba-pl010.c | 2 | ||||
-rw-r--r-- | drivers/serial/amba-pl011.c | 2 | ||||
-rw-r--r-- | drivers/serial/imx.c | 8 | ||||
-rw-r--r-- | drivers/serial/pxa.c | 3 | ||||
-rw-r--r-- | drivers/serial/s3c24a0.c | 118 | ||||
-rw-r--r-- | drivers/serial/s3c6400.c | 151 | ||||
-rw-r--r-- | drivers/serial/samsung.c | 184 | ||||
-rw-r--r-- | drivers/serial/samsung.h | 9 | ||||
-rw-r--r-- | drivers/serial/serial_lh7a40x.c | 3 |
11 files changed, 461 insertions, 46 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 579d63a81aa2..b695ab3142d8 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -447,7 +447,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on ARM && PLAT_S3C24XX + depends on ARM && PLAT_S3C select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -455,6 +455,16 @@ config SERIAL_SAMSUNG provide all of these ports, depending on how the serial port pins are configured. +config SERIAL_SAMSUNG_UARTS + int + depends on SERIAL_SAMSUNG + default 2 if ARCH_S3C2400 + default 4 if ARCH_S3C64XX || CPU_S3C2443 + default 3 + help + Select the number of available UART ports for the Samsung S3C + serial driver + config SERIAL_SAMSUNG_DEBUG bool "Samsung SoC serial debug" depends on SERIAL_SAMSUNG && DEBUG_LL @@ -508,7 +518,20 @@ config SERIAL_S3C2440 help Serial port support for the Samsung S3C2440 and S3C2442 SoC +config SERIAL_S3C24A0 + tristate "Samsung S3C24A0 Serial port support" + depends on SERIAL_SAMSUNG && CPU_S3C24A0 + default y if CPU_S3C24A0 + help + Serial port support for the Samsung S3C24A0 SoC +config SERIAL_S3C6400 + tristate "Samsung S3C6400/S3C6410 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410) + default y + help + Serial port support for the Samsung S3C6400 and S3C6410 + SoCs config SERIAL_DZ bool "DECstation DZ serial driver" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 0c17c8ddb19d..dfe775ac45b2 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -41,6 +41,8 @@ obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o +obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o +obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 71562689116f..e3a5ad5ef1d6 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -692,7 +692,7 @@ static int pl010_probe(struct amba_device *dev, void *id) goto free; } - uap->clk = clk_get(&dev->dev, "UARTCLK"); + uap->clk = clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); goto unmap; diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index b7180046f8db..8b2b9700f3e4 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -756,7 +756,7 @@ static int pl011_probe(struct amba_device *dev, void *id) goto free; } - uap->clk = clk_get(&dev->dev, "UARTCLK"); + uap->clk = clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); goto unmap; diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 3f90f1bbbbcd..a50954612b60 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -66,7 +66,7 @@ #define ONEMS 0xb0 /* One Millisecond register */ #define UTS 0xb4 /* UART Test Register */ #endif -#ifdef CONFIG_ARCH_IMX +#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1) #define BIPR1 0xb0 /* Incremental Preset Register 1 */ #define BIPR2 0xb4 /* Incremental Preset Register 2 */ #define BIPR3 0xb8 /* Incremental Preset Register 3 */ @@ -96,7 +96,7 @@ #define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ #define UCR1_SNDBRK (1<<4) /* Send break */ #define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ -#ifdef CONFIG_ARCH_IMX +#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1) #define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */ #endif #if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 @@ -187,11 +187,11 @@ #define MAX_INTERNAL_IRQ IMX_IRQS #endif -#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 +#ifdef CONFIG_ARCH_MXC #define SERIAL_IMX_MAJOR 207 #define MINOR_START 16 #define DEV_NAME "ttymxc" -#define MAX_INTERNAL_IRQ MXC_MAX_INT_LINES +#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS #endif /* diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index abc00be55433..f6e3b86bb0be 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -48,6 +48,7 @@ #include <mach/hardware.h> #include <asm/irq.h> #include <mach/pxa-regs.h> +#include <mach/regs-uart.h> struct uart_pxa_port { @@ -766,7 +767,7 @@ static int serial_pxa_probe(struct platform_device *dev) if (!sport) return -ENOMEM; - sport->clk = clk_get(&dev->dev, "UARTCLK"); + sport->clk = clk_get(&dev->dev, NULL); if (IS_ERR(sport->clk)) { ret = PTR_ERR(sport->clk); goto err_free; diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c new file mode 100644 index 000000000000..ebf2fd3c8f7d --- /dev/null +++ b/drivers/serial/s3c24a0.c @@ -0,0 +1,118 @@ +/* linux/drivers/serial/s3c24a0.c + * + * Driver for Samsung S3C24A0 SoC onboard UARTs. + * + * Based on drivers/serial/s3c2410.c + * + * Author: Sandeep Patil <sandeep.patil@azingo.com> + * + * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/io.h> +#include <linux/irq.h> + +#include <mach/hardware.h> + +#include <plat/regs-serial.h> +#include <mach/regs-gpio.h> + +#include "samsung.h" + +static int s3c24a0_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "uclk") == 0) + ucon |= S3C2410_UCON_UCLK; + else + ucon &= ~S3C2410_UCON_UCLK; + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + +static int s3c24a0_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk"; + + return 0; +} + +static int s3c24a0_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + wr_regl(port, S3C2410_UCON, cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c24a0_uart_inf = { + .name = "Samsung S3C24A0 UART", + .type = PORT_S3C2410, + .fifosize = 16, + .rx_fifomask = S3C24A0_UFSTAT_RXMASK, + .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT, + .rx_fifofull = S3C24A0_UFSTAT_RXFULL, + .tx_fifofull = S3C24A0_UFSTAT_TXFULL, + .tx_fifomask = S3C24A0_UFSTAT_TXMASK, + .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT, + .get_clksrc = s3c24a0_serial_getsource, + .set_clksrc = s3c24a0_serial_setsource, + .reset_port = s3c24a0_serial_resetport, +}; + +static int s3c24a0_serial_probe(struct platform_device *dev) +{ + return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf); +} + +static struct platform_driver s3c24a0_serial_drv = { + .probe = s3c24a0_serial_probe, + .remove = s3c24xx_serial_remove, + .driver = { + .name = "s3c24a0-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf); + +static int __init s3c24a0_serial_init(void) +{ + return s3c24xx_serial_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf); +} + +static void __exit s3c24a0_serial_exit(void) +{ + platform_driver_unregister(&s3c24a0_serial_drv); +} + +module_init(s3c24a0_serial_init); +module_exit(s3c24a0_serial_exit); + diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c new file mode 100644 index 000000000000..06936d13393f --- /dev/null +++ b/drivers/serial/s3c6400.c @@ -0,0 +1,151 @@ +/* linux/drivers/serial/s3c6400.c + * + * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs. + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial_core.h> +#include <linux/serial.h> + +#include <asm/irq.h> +#include <mach/hardware.h> + +#include <plat/regs-serial.h> + +#include "samsung.h" + +static int s3c6400_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "uclk0") == 0) { + ucon &= ~S3C6400_UCON_CLKMASK; + ucon |= S3C6400_UCON_UCLK0; + } else if (strcmp(clk->name, "uclk1") == 0) + ucon |= S3C6400_UCON_UCLK1; + else if (strcmp(clk->name, "pclk") == 0) { + /* See notes about transitioning from UCLK to PCLK */ + ucon &= ~S3C6400_UCON_UCLK0; + } else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s3c6400_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + u32 ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + + switch (ucon & S3C6400_UCON_CLKMASK) { + case S3C6400_UCON_UCLK0: + clk->name = "uclk0"; + break; + + case S3C6400_UCON_UCLK1: + clk->name = "uclk1"; + break; + + case S3C6400_UCON_PCLK: + case S3C6400_UCON_PCLK2: + clk->name = "pclk"; + break; + } + + return 0; +} + +static int s3c6400_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + /* ensure we don't change the clock settings... */ + + ucon &= S3C6400_UCON_CLKMASK; + + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c6400_uart_inf = { + .name = "Samsung S3C6400 UART", + .type = PORT_S3C6400, + .fifosize = 64, + .rx_fifomask = S3C2440_UFSTAT_RXMASK, + .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2440_UFSTAT_RXFULL, + .tx_fifofull = S3C2440_UFSTAT_TXFULL, + .tx_fifomask = S3C2440_UFSTAT_TXMASK, + .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, + .get_clksrc = s3c6400_serial_getsource, + .set_clksrc = s3c6400_serial_setsource, + .reset_port = s3c6400_serial_resetport, +}; + +/* device management */ + +static int s3c6400_serial_probe(struct platform_device *dev) +{ + dbg("s3c6400_serial_probe: dev=%p\n", dev); + return s3c24xx_serial_probe(dev, &s3c6400_uart_inf); +} + +static struct platform_driver s3c6400_serial_drv = { + .probe = s3c6400_serial_probe, + .remove = s3c24xx_serial_remove, + .driver = { + .name = "s3c6400-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c6400_serial_drv, &s3c6400_uart_inf); + +static int __init s3c6400_serial_init(void) +{ + return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf); +} + +static void __exit s3c6400_serial_exit(void) +{ + platform_driver_unregister(&s3c6400_serial_drv); +} + +module_init(s3c6400_serial_init); +module_exit(s3c6400_serial_exit); + +MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:s3c6400-uart"); diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 1e219d3d0352..41ac94872b8d 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -42,13 +42,14 @@ #include <linux/serial.h> #include <linux/delay.h> #include <linux/clk.h> +#include <linux/cpufreq.h> #include <asm/irq.h> #include <mach/hardware.h> +#include <mach/map.h> #include <plat/regs-serial.h> -#include <mach/regs-gpio.h> #include "samsung.h" @@ -58,19 +59,6 @@ #define S3C24XX_SERIAL_MAJOR 204 #define S3C24XX_SERIAL_MINOR 64 -/* we can support 3 uarts, but not always use them */ - -#ifdef CONFIG_CPU_S3C2400 -#define NR_PORTS (2) -#else -#define NR_PORTS (3) -#endif - -/* port irq numbers */ - -#define TX_IRQ(port) ((port)->irq + 1) -#define RX_IRQ(port) ((port)->irq) - /* macros to change one thing to another */ #define tx_enabled(port) ((port)->unused[0]) @@ -136,8 +124,10 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port) static void s3c24xx_serial_stop_tx(struct uart_port *port) { + struct s3c24xx_uart_port *ourport = to_ourport(port); + if (tx_enabled(port)) { - disable_irq(TX_IRQ(port)); + disable_irq(ourport->tx_irq); tx_enabled(port) = 0; if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_enable(port); @@ -146,11 +136,13 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) static void s3c24xx_serial_start_tx(struct uart_port *port) { + struct s3c24xx_uart_port *ourport = to_ourport(port); + if (!tx_enabled(port)) { if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_disable(port); - enable_irq(TX_IRQ(port)); + enable_irq(ourport->tx_irq); tx_enabled(port) = 1; } } @@ -158,9 +150,11 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) static void s3c24xx_serial_stop_rx(struct uart_port *port) { + struct s3c24xx_uart_port *ourport = to_ourport(port); + if (rx_enabled(port)) { dbg("s3c24xx_serial_stop_rx: port=%p\n", port); - disable_irq(RX_IRQ(port)); + disable_irq(ourport->rx_irq); rx_enabled(port) = 0; } } @@ -384,13 +378,13 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (ourport->tx_claimed) { - free_irq(TX_IRQ(port), ourport); + free_irq(ourport->tx_irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; } if (ourport->rx_claimed) { - free_irq(RX_IRQ(port), ourport); + free_irq(ourport->rx_irq, ourport); ourport->rx_claimed = 0; rx_enabled(port) = 0; } @@ -407,12 +401,11 @@ static int s3c24xx_serial_startup(struct uart_port *port) rx_enabled(port) = 1; - ret = request_irq(RX_IRQ(port), - s3c24xx_serial_rx_chars, 0, + ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, s3c24xx_serial_portname(port), ourport); if (ret != 0) { - printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port)); + printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); return ret; } @@ -422,12 +415,11 @@ static int s3c24xx_serial_startup(struct uart_port *port) tx_enabled(port) = 1; - ret = request_irq(TX_IRQ(port), - s3c24xx_serial_tx_chars, 0, + ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, s3c24xx_serial_portname(port), ourport); if (ret) { - printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port)); + printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); goto err; } @@ -452,6 +444,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, { struct s3c24xx_uart_port *ourport = to_ourport(port); + ourport->pm_level = level; + switch (level) { case 3: if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) @@ -661,6 +655,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ourport->clksrc = clksrc; ourport->baudclk = clk; + ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; } switch (termios->c_cflag & CSIZE) { @@ -752,6 +747,8 @@ static const char *s3c24xx_serial_type(struct uart_port *port) return "S3C2440"; case PORT_S3C2412: return "S3C2412"; + case PORT_S3C6400: + return "S3C6400/10"; default: return NULL; } @@ -827,14 +824,14 @@ static struct uart_ops s3c24xx_serial_ops = { static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .dev_name = "s3c2410_serial", - .nr = 3, + .nr = CONFIG_SERIAL_SAMSUNG_UARTS, .cons = S3C24XX_SERIAL_CONSOLE, .driver_name = S3C24XX_SERIAL_NAME, .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR, }; -static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { +static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { [0] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), @@ -859,7 +856,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { .line = 1, } }, -#if NR_PORTS > 2 +#if CONFIG_SERIAL_SAMSUNG_UARTS > 2 [2] = { .port = { @@ -872,6 +869,20 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { .flags = UPF_BOOT_AUTOCONF, .line = 2, } + }, +#endif +#if CONFIG_SERIAL_SAMSUNG_UARTS > 3 + [3] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX3, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 3, + } } #endif }; @@ -890,6 +901,89 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port, return (info->reset_port)(port, cfg); } + +#ifdef CONFIG_CPU_FREQ + +static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c24xx_uart_port *port; + struct uart_port *uport; + + port = container_of(nb, struct s3c24xx_uart_port, freq_transition); + uport = &port->port; + + /* check to see if port is enabled */ + + if (port->pm_level != 0) + return 0; + + /* try and work out if the baudrate is changing, we can detect + * a change in rate, but we do not have support for detecting + * a disturbance in the clock-rate over the change. + */ + + if (IS_ERR(port->clk)) + goto exit; + + if (port->baudclk_rate == clk_get_rate(port->clk)) + goto exit; + + if (val == CPUFREQ_PRECHANGE) { + /* we should really shut the port down whilst the + * frequency change is in progress. */ + + } else if (val == CPUFREQ_POSTCHANGE) { + struct ktermios *termios; + struct tty_struct *tty; + + if (uport->info == NULL) + goto exit; + + tty = uport->info->port.tty; + + if (tty == NULL) + goto exit; + + termios = tty->termios; + + if (termios == NULL) { + printk(KERN_WARNING "%s: no termios?\n", __func__); + goto exit; + } + + s3c24xx_serial_set_termios(uport, termios, NULL); + } + + exit: + return 0; +} + +static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) +{ + port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; + + return cpufreq_register_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) +{ + cpufreq_unregister_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) +{ + return 0; +} + +static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) +{ +} +#endif + /* s3c24xx_serial_init_port * * initialise a single serial port from the platform device given @@ -914,8 +1008,11 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, if (port->mapbase != 0) return 0; - if (cfg->hwport > 3) - return -EINVAL; + if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) { + printk(KERN_ERR "%s: port %d bigger than %d\n", __func__, + cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS); + return -ERANGE; + } /* setup info for port */ port->dev = &platdev->dev; @@ -943,18 +1040,26 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); - port->mapbase = res->start; - port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART); + port->mapbase = res->start; + port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000); ret = platform_get_irq(platdev, 0); if (ret < 0) port->irq = 0; - else + else { port->irq = ret; + ourport->rx_irq = ret; + ourport->tx_irq = ret + 1; + } + + ret = platform_get_irq(platdev, 1); + if (ret > 0) + ourport->tx_irq = ret; ourport->clk = clk_get(&platdev->dev, "uart"); - dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n", - port->mapbase, port->membase, port->irq, port->uartclk); + dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", + port->mapbase, port->membase, port->irq, + ourport->rx_irq, ourport->tx_irq, port->uartclk); /* reset the fifos (and setup the uart) */ s3c24xx_serial_resetport(port, cfg); @@ -1002,6 +1107,10 @@ int s3c24xx_serial_probe(struct platform_device *dev, if (ret < 0) printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); + ret = s3c24xx_serial_cpufreq_register(ourport); + if (ret < 0) + dev_err(&dev->dev, "failed to add cpufreq notifier\n"); + return 0; probe_err: @@ -1015,6 +1124,7 @@ int s3c24xx_serial_remove(struct platform_device *dev) struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); if (port) { + s3c24xx_serial_cpufreq_deregister(to_ourport(port)); device_remove_file(&dev->dev, &dev_attr_clock_source); uart_remove_one_port(&s3c24xx_uart_drv, port); } @@ -1219,7 +1329,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) platdev_ptr = s3c24xx_uart_devs; - for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) { + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { s3c24xx_serial_init_port(ptr, info, *platdev_ptr); } @@ -1240,7 +1350,7 @@ s3c24xx_serial_console_setup(struct console *co, char *options) /* is this a valid port */ - if (co->index == -1 || co->index >= NR_PORTS) + if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS) co->index = 0; port = &s3c24xx_serial_ports[co->index].port; diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index 5c92ebbe7d9e..571d6b90d206 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h @@ -33,12 +33,21 @@ struct s3c24xx_uart_info { struct s3c24xx_uart_port { unsigned char rx_claimed; unsigned char tx_claimed; + unsigned int pm_level; + unsigned long baudclk_rate; + + unsigned int rx_irq; + unsigned int tx_irq; struct s3c24xx_uart_info *info; struct s3c24xx_uart_clksrc *clksrc; struct clk *clk; struct clk *baudclk; struct uart_port port; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif }; /* conversion functions */ diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c index 61dc8b3daa26..a7bf024a8286 100644 --- a/drivers/serial/serial_lh7a40x.c +++ b/drivers/serial/serial_lh7a40x.c @@ -41,9 +41,10 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/io.h> -#include <asm/io.h> #include <asm/irq.h> +#include <mach/hardware.h> #define DEV_MAJOR 204 #define DEV_MINOR 16 |