aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2008-12-18 17:15:30 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-12-18 17:15:30 -0500
commitfdb0a1a67e131f07a308730c80d07e330211d2e0 (patch)
tree4fbf35055fc965737c1ffdbcfe98d7ea8335f940 /drivers
parentfc0b7a20368193e1f7cf1376624eed419b9c33e6 (diff)
parent7bb56d01f111890414fbe35412698485b68ed468 (diff)
Merge branch 'next-merged' of git://aeryn.fluff.org.uk/bjdooks/linux into devel
Diffstat (limited to 'drivers')
-rw-r--r--drivers/serial/Kconfig25
-rw-r--r--drivers/serial/Makefile2
-rw-r--r--drivers/serial/s3c24a0.c118
-rw-r--r--drivers/serial/s3c6400.c151
-rw-r--r--drivers/serial/samsung.c184
-rw-r--r--drivers/serial/samsung.h9
6 files changed, 451 insertions, 38 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
447 447
448config SERIAL_SAMSUNG 448config SERIAL_SAMSUNG
449 tristate "Samsung SoC serial support" 449 tristate "Samsung SoC serial support"
450 depends on ARM && PLAT_S3C24XX 450 depends on ARM && PLAT_S3C
451 select SERIAL_CORE 451 select SERIAL_CORE
452 help 452 help
453 Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, 453 Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
@@ -455,6 +455,16 @@ config SERIAL_SAMSUNG
455 provide all of these ports, depending on how the serial port 455 provide all of these ports, depending on how the serial port
456 pins are configured. 456 pins are configured.
457 457
458config SERIAL_SAMSUNG_UARTS
459 int
460 depends on SERIAL_SAMSUNG
461 default 2 if ARCH_S3C2400
462 default 4 if ARCH_S3C64XX || CPU_S3C2443
463 default 3
464 help
465 Select the number of available UART ports for the Samsung S3C
466 serial driver
467
458config SERIAL_SAMSUNG_DEBUG 468config SERIAL_SAMSUNG_DEBUG
459 bool "Samsung SoC serial debug" 469 bool "Samsung SoC serial debug"
460 depends on SERIAL_SAMSUNG && DEBUG_LL 470 depends on SERIAL_SAMSUNG && DEBUG_LL
@@ -508,7 +518,20 @@ config SERIAL_S3C2440
508 help 518 help
509 Serial port support for the Samsung S3C2440 and S3C2442 SoC 519 Serial port support for the Samsung S3C2440 and S3C2442 SoC
510 520
521config SERIAL_S3C24A0
522 tristate "Samsung S3C24A0 Serial port support"
523 depends on SERIAL_SAMSUNG && CPU_S3C24A0
524 default y if CPU_S3C24A0
525 help
526 Serial port support for the Samsung S3C24A0 SoC
511 527
528config SERIAL_S3C6400
529 tristate "Samsung S3C6400/S3C6410 Serial port support"
530 depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410)
531 default y
532 help
533 Serial port support for the Samsung S3C6400 and S3C6410
534 SoCs
512 535
513config SERIAL_DZ 536config SERIAL_DZ
514 bool "DECstation DZ serial driver" 537 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
41obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o 41obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
42obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o 42obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
43obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o 43obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
44obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o
45obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
44obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o 46obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
45obj-$(CONFIG_SERIAL_MUX) += mux.o 47obj-$(CONFIG_SERIAL_MUX) += mux.o
46obj-$(CONFIG_SERIAL_68328) += 68328serial.o 48obj-$(CONFIG_SERIAL_68328) += 68328serial.o
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 @@
1/* linux/drivers/serial/s3c24a0.c
2 *
3 * Driver for Samsung S3C24A0 SoC onboard UARTs.
4 *
5 * Based on drivers/serial/s3c2410.c
6 *
7 * Author: Sandeep Patil <sandeep.patil@azingo.com>
8 *
9 * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
10 * http://armlinux.simtec.co.uk/
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15*/
16
17#include <linux/module.h>
18#include <linux/ioport.h>
19#include <linux/platform_device.h>
20#include <linux/init.h>
21#include <linux/serial_core.h>
22#include <linux/serial.h>
23#include <linux/io.h>
24#include <linux/irq.h>
25
26#include <mach/hardware.h>
27
28#include <plat/regs-serial.h>
29#include <mach/regs-gpio.h>
30
31#include "samsung.h"
32
33static int s3c24a0_serial_setsource(struct uart_port *port,
34 struct s3c24xx_uart_clksrc *clk)
35{
36 unsigned long ucon = rd_regl(port, S3C2410_UCON);
37
38 if (strcmp(clk->name, "uclk") == 0)
39 ucon |= S3C2410_UCON_UCLK;
40 else
41 ucon &= ~S3C2410_UCON_UCLK;
42
43 wr_regl(port, S3C2410_UCON, ucon);
44 return 0;
45}
46
47static int s3c24a0_serial_getsource(struct uart_port *port,
48 struct s3c24xx_uart_clksrc *clk)
49{
50 unsigned long ucon = rd_regl(port, S3C2410_UCON);
51
52 clk->divisor = 1;
53 clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk";
54
55 return 0;
56}
57
58static int s3c24a0_serial_resetport(struct uart_port *port,
59 struct s3c2410_uartcfg *cfg)
60{
61 dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n",
62 port, port->mapbase, cfg);
63
64 wr_regl(port, S3C2410_UCON, cfg->ucon);
65 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
66
67 /* reset both fifos */
68
69 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
70 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
71
72 return 0;
73}
74
75static struct s3c24xx_uart_info s3c24a0_uart_inf = {
76 .name = "Samsung S3C24A0 UART",
77 .type = PORT_S3C2410,
78 .fifosize = 16,
79 .rx_fifomask = S3C24A0_UFSTAT_RXMASK,
80 .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT,
81 .rx_fifofull = S3C24A0_UFSTAT_RXFULL,
82 .tx_fifofull = S3C24A0_UFSTAT_TXFULL,
83 .tx_fifomask = S3C24A0_UFSTAT_TXMASK,
84 .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT,
85 .get_clksrc = s3c24a0_serial_getsource,
86 .set_clksrc = s3c24a0_serial_setsource,
87 .reset_port = s3c24a0_serial_resetport,
88};
89
90static int s3c24a0_serial_probe(struct platform_device *dev)
91{
92 return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf);
93}
94
95static struct platform_driver s3c24a0_serial_drv = {
96 .probe = s3c24a0_serial_probe,
97 .remove = s3c24xx_serial_remove,
98 .driver = {
99 .name = "s3c24a0-uart",
100 .owner = THIS_MODULE,
101 },
102};
103
104s3c24xx_console_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
105
106static int __init s3c24a0_serial_init(void)
107{
108 return s3c24xx_serial_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
109}
110
111static void __exit s3c24a0_serial_exit(void)
112{
113 platform_driver_unregister(&s3c24a0_serial_drv);
114}
115
116module_init(s3c24a0_serial_init);
117module_exit(s3c24a0_serial_exit);
118
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 @@
1/* linux/drivers/serial/s3c6400.c
2 *
3 * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs.
4 *
5 * Copyright 2008 Openmoko, Inc.
6 * Copyright 2008 Simtec Electronics
7 * Ben Dooks <ben@simtec.co.uk>
8 * http://armlinux.simtec.co.uk/
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/module.h>
16#include <linux/ioport.h>
17#include <linux/io.h>
18#include <linux/platform_device.h>
19#include <linux/init.h>
20#include <linux/serial_core.h>
21#include <linux/serial.h>
22
23#include <asm/irq.h>
24#include <mach/hardware.h>
25
26#include <plat/regs-serial.h>
27
28#include "samsung.h"
29
30static int s3c6400_serial_setsource(struct uart_port *port,
31 struct s3c24xx_uart_clksrc *clk)
32{
33 unsigned long ucon = rd_regl(port, S3C2410_UCON);
34
35 if (strcmp(clk->name, "uclk0") == 0) {
36 ucon &= ~S3C6400_UCON_CLKMASK;
37 ucon |= S3C6400_UCON_UCLK0;
38 } else if (strcmp(clk->name, "uclk1") == 0)
39 ucon |= S3C6400_UCON_UCLK1;
40 else if (strcmp(clk->name, "pclk") == 0) {
41 /* See notes about transitioning from UCLK to PCLK */
42 ucon &= ~S3C6400_UCON_UCLK0;
43 } else {
44 printk(KERN_ERR "unknown clock source %s\n", clk->name);
45 return -EINVAL;
46 }
47
48 wr_regl(port, S3C2410_UCON, ucon);
49 return 0;
50}
51
52
53static int s3c6400_serial_getsource(struct uart_port *port,
54 struct s3c24xx_uart_clksrc *clk)
55{
56 u32 ucon = rd_regl(port, S3C2410_UCON);
57
58 clk->divisor = 1;
59
60 switch (ucon & S3C6400_UCON_CLKMASK) {
61 case S3C6400_UCON_UCLK0:
62 clk->name = "uclk0";
63 break;
64
65 case S3C6400_UCON_UCLK1:
66 clk->name = "uclk1";
67 break;
68
69 case S3C6400_UCON_PCLK:
70 case S3C6400_UCON_PCLK2:
71 clk->name = "pclk";
72 break;
73 }
74
75 return 0;
76}
77
78static int s3c6400_serial_resetport(struct uart_port *port,
79 struct s3c2410_uartcfg *cfg)
80{
81 unsigned long ucon = rd_regl(port, S3C2410_UCON);
82
83 dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n",
84 port, port->mapbase, cfg);
85
86 /* ensure we don't change the clock settings... */
87
88 ucon &= S3C6400_UCON_CLKMASK;
89
90 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
91 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
92
93 /* reset both fifos */
94
95 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
96 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
97
98 return 0;
99}
100
101static struct s3c24xx_uart_info s3c6400_uart_inf = {
102 .name = "Samsung S3C6400 UART",
103 .type = PORT_S3C6400,
104 .fifosize = 64,
105 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
106 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
107 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
108 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
109 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
110 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
111 .get_clksrc = s3c6400_serial_getsource,
112 .set_clksrc = s3c6400_serial_setsource,
113 .reset_port = s3c6400_serial_resetport,
114};
115
116/* device management */
117
118static int s3c6400_serial_probe(struct platform_device *dev)
119{
120 dbg("s3c6400_serial_probe: dev=%p\n", dev);
121 return s3c24xx_serial_probe(dev, &s3c6400_uart_inf);
122}
123
124static struct platform_driver s3c6400_serial_drv = {
125 .probe = s3c6400_serial_probe,
126 .remove = s3c24xx_serial_remove,
127 .driver = {
128 .name = "s3c6400-uart",
129 .owner = THIS_MODULE,
130 },
131};
132
133s3c24xx_console_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
134
135static int __init s3c6400_serial_init(void)
136{
137 return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
138}
139
140static void __exit s3c6400_serial_exit(void)
141{
142 platform_driver_unregister(&s3c6400_serial_drv);
143}
144
145module_init(s3c6400_serial_init);
146module_exit(s3c6400_serial_exit);
147
148MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver");
149MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
150MODULE_LICENSE("GPL v2");
151MODULE_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 @@
42#include <linux/serial.h> 42#include <linux/serial.h>
43#include <linux/delay.h> 43#include <linux/delay.h>
44#include <linux/clk.h> 44#include <linux/clk.h>
45#include <linux/cpufreq.h>
45 46
46#include <asm/irq.h> 47#include <asm/irq.h>
47 48
48#include <mach/hardware.h> 49#include <mach/hardware.h>
50#include <mach/map.h>
49 51
50#include <plat/regs-serial.h> 52#include <plat/regs-serial.h>
51#include <mach/regs-gpio.h>
52 53
53#include "samsung.h" 54#include "samsung.h"
54 55
@@ -58,19 +59,6 @@
58#define S3C24XX_SERIAL_MAJOR 204 59#define S3C24XX_SERIAL_MAJOR 204
59#define S3C24XX_SERIAL_MINOR 64 60#define S3C24XX_SERIAL_MINOR 64
60 61
61/* we can support 3 uarts, but not always use them */
62
63#ifdef CONFIG_CPU_S3C2400
64#define NR_PORTS (2)
65#else
66#define NR_PORTS (3)
67#endif
68
69/* port irq numbers */
70
71#define TX_IRQ(port) ((port)->irq + 1)
72#define RX_IRQ(port) ((port)->irq)
73
74/* macros to change one thing to another */ 62/* macros to change one thing to another */
75 63
76#define tx_enabled(port) ((port)->unused[0]) 64#define tx_enabled(port) ((port)->unused[0])
@@ -136,8 +124,10 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port)
136 124
137static void s3c24xx_serial_stop_tx(struct uart_port *port) 125static void s3c24xx_serial_stop_tx(struct uart_port *port)
138{ 126{
127 struct s3c24xx_uart_port *ourport = to_ourport(port);
128
139 if (tx_enabled(port)) { 129 if (tx_enabled(port)) {
140 disable_irq(TX_IRQ(port)); 130 disable_irq(ourport->tx_irq);
141 tx_enabled(port) = 0; 131 tx_enabled(port) = 0;
142 if (port->flags & UPF_CONS_FLOW) 132 if (port->flags & UPF_CONS_FLOW)
143 s3c24xx_serial_rx_enable(port); 133 s3c24xx_serial_rx_enable(port);
@@ -146,11 +136,13 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
146 136
147static void s3c24xx_serial_start_tx(struct uart_port *port) 137static void s3c24xx_serial_start_tx(struct uart_port *port)
148{ 138{
139 struct s3c24xx_uart_port *ourport = to_ourport(port);
140
149 if (!tx_enabled(port)) { 141 if (!tx_enabled(port)) {
150 if (port->flags & UPF_CONS_FLOW) 142 if (port->flags & UPF_CONS_FLOW)
151 s3c24xx_serial_rx_disable(port); 143 s3c24xx_serial_rx_disable(port);
152 144
153 enable_irq(TX_IRQ(port)); 145 enable_irq(ourport->tx_irq);
154 tx_enabled(port) = 1; 146 tx_enabled(port) = 1;
155 } 147 }
156} 148}
@@ -158,9 +150,11 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
158 150
159static void s3c24xx_serial_stop_rx(struct uart_port *port) 151static void s3c24xx_serial_stop_rx(struct uart_port *port)
160{ 152{
153 struct s3c24xx_uart_port *ourport = to_ourport(port);
154
161 if (rx_enabled(port)) { 155 if (rx_enabled(port)) {
162 dbg("s3c24xx_serial_stop_rx: port=%p\n", port); 156 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
163 disable_irq(RX_IRQ(port)); 157 disable_irq(ourport->rx_irq);
164 rx_enabled(port) = 0; 158 rx_enabled(port) = 0;
165 } 159 }
166} 160}
@@ -384,13 +378,13 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
384 struct s3c24xx_uart_port *ourport = to_ourport(port); 378 struct s3c24xx_uart_port *ourport = to_ourport(port);
385 379
386 if (ourport->tx_claimed) { 380 if (ourport->tx_claimed) {
387 free_irq(TX_IRQ(port), ourport); 381 free_irq(ourport->tx_irq, ourport);
388 tx_enabled(port) = 0; 382 tx_enabled(port) = 0;
389 ourport->tx_claimed = 0; 383 ourport->tx_claimed = 0;
390 } 384 }
391 385
392 if (ourport->rx_claimed) { 386 if (ourport->rx_claimed) {
393 free_irq(RX_IRQ(port), ourport); 387 free_irq(ourport->rx_irq, ourport);
394 ourport->rx_claimed = 0; 388 ourport->rx_claimed = 0;
395 rx_enabled(port) = 0; 389 rx_enabled(port) = 0;
396 } 390 }
@@ -407,12 +401,11 @@ static int s3c24xx_serial_startup(struct uart_port *port)
407 401
408 rx_enabled(port) = 1; 402 rx_enabled(port) = 1;
409 403
410 ret = request_irq(RX_IRQ(port), 404 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
411 s3c24xx_serial_rx_chars, 0,
412 s3c24xx_serial_portname(port), ourport); 405 s3c24xx_serial_portname(port), ourport);
413 406
414 if (ret != 0) { 407 if (ret != 0) {
415 printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port)); 408 printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
416 return ret; 409 return ret;
417 } 410 }
418 411
@@ -422,12 +415,11 @@ static int s3c24xx_serial_startup(struct uart_port *port)
422 415
423 tx_enabled(port) = 1; 416 tx_enabled(port) = 1;
424 417
425 ret = request_irq(TX_IRQ(port), 418 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
426 s3c24xx_serial_tx_chars, 0,
427 s3c24xx_serial_portname(port), ourport); 419 s3c24xx_serial_portname(port), ourport);
428 420
429 if (ret) { 421 if (ret) {
430 printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port)); 422 printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
431 goto err; 423 goto err;
432 } 424 }
433 425
@@ -452,6 +444,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
452{ 444{
453 struct s3c24xx_uart_port *ourport = to_ourport(port); 445 struct s3c24xx_uart_port *ourport = to_ourport(port);
454 446
447 ourport->pm_level = level;
448
455 switch (level) { 449 switch (level) {
456 case 3: 450 case 3:
457 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) 451 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
@@ -661,6 +655,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
661 655
662 ourport->clksrc = clksrc; 656 ourport->clksrc = clksrc;
663 ourport->baudclk = clk; 657 ourport->baudclk = clk;
658 ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
664 } 659 }
665 660
666 switch (termios->c_cflag & CSIZE) { 661 switch (termios->c_cflag & CSIZE) {
@@ -752,6 +747,8 @@ static const char *s3c24xx_serial_type(struct uart_port *port)
752 return "S3C2440"; 747 return "S3C2440";
753 case PORT_S3C2412: 748 case PORT_S3C2412:
754 return "S3C2412"; 749 return "S3C2412";
750 case PORT_S3C6400:
751 return "S3C6400/10";
755 default: 752 default:
756 return NULL; 753 return NULL;
757 } 754 }
@@ -827,14 +824,14 @@ static struct uart_ops s3c24xx_serial_ops = {
827static struct uart_driver s3c24xx_uart_drv = { 824static struct uart_driver s3c24xx_uart_drv = {
828 .owner = THIS_MODULE, 825 .owner = THIS_MODULE,
829 .dev_name = "s3c2410_serial", 826 .dev_name = "s3c2410_serial",
830 .nr = 3, 827 .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
831 .cons = S3C24XX_SERIAL_CONSOLE, 828 .cons = S3C24XX_SERIAL_CONSOLE,
832 .driver_name = S3C24XX_SERIAL_NAME, 829 .driver_name = S3C24XX_SERIAL_NAME,
833 .major = S3C24XX_SERIAL_MAJOR, 830 .major = S3C24XX_SERIAL_MAJOR,
834 .minor = S3C24XX_SERIAL_MINOR, 831 .minor = S3C24XX_SERIAL_MINOR,
835}; 832};
836 833
837static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { 834static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
838 [0] = { 835 [0] = {
839 .port = { 836 .port = {
840 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), 837 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
@@ -859,7 +856,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
859 .line = 1, 856 .line = 1,
860 } 857 }
861 }, 858 },
862#if NR_PORTS > 2 859#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
863 860
864 [2] = { 861 [2] = {
865 .port = { 862 .port = {
@@ -872,6 +869,20 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
872 .flags = UPF_BOOT_AUTOCONF, 869 .flags = UPF_BOOT_AUTOCONF,
873 .line = 2, 870 .line = 2,
874 } 871 }
872 },
873#endif
874#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
875 [3] = {
876 .port = {
877 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
878 .iotype = UPIO_MEM,
879 .irq = IRQ_S3CUART_RX3,
880 .uartclk = 0,
881 .fifosize = 16,
882 .ops = &s3c24xx_serial_ops,
883 .flags = UPF_BOOT_AUTOCONF,
884 .line = 3,
885 }
875 } 886 }
876#endif 887#endif
877}; 888};
@@ -890,6 +901,89 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port,
890 return (info->reset_port)(port, cfg); 901 return (info->reset_port)(port, cfg);
891} 902}
892 903
904
905#ifdef CONFIG_CPU_FREQ
906
907static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
908 unsigned long val, void *data)
909{
910 struct s3c24xx_uart_port *port;
911 struct uart_port *uport;
912
913 port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
914 uport = &port->port;
915
916 /* check to see if port is enabled */
917
918 if (port->pm_level != 0)
919 return 0;
920
921 /* try and work out if the baudrate is changing, we can detect
922 * a change in rate, but we do not have support for detecting
923 * a disturbance in the clock-rate over the change.
924 */
925
926 if (IS_ERR(port->clk))
927 goto exit;
928
929 if (port->baudclk_rate == clk_get_rate(port->clk))
930 goto exit;
931
932 if (val == CPUFREQ_PRECHANGE) {
933 /* we should really shut the port down whilst the
934 * frequency change is in progress. */
935
936 } else if (val == CPUFREQ_POSTCHANGE) {
937 struct ktermios *termios;
938 struct tty_struct *tty;
939
940 if (uport->info == NULL)
941 goto exit;
942
943 tty = uport->info->port.tty;
944
945 if (tty == NULL)
946 goto exit;
947
948 termios = tty->termios;
949
950 if (termios == NULL) {
951 printk(KERN_WARNING "%s: no termios?\n", __func__);
952 goto exit;
953 }
954
955 s3c24xx_serial_set_termios(uport, termios, NULL);
956 }
957
958 exit:
959 return 0;
960}
961
962static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
963{
964 port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
965
966 return cpufreq_register_notifier(&port->freq_transition,
967 CPUFREQ_TRANSITION_NOTIFIER);
968}
969
970static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
971{
972 cpufreq_unregister_notifier(&port->freq_transition,
973 CPUFREQ_TRANSITION_NOTIFIER);
974}
975
976#else
977static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
978{
979 return 0;
980}
981
982static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
983{
984}
985#endif
986
893/* s3c24xx_serial_init_port 987/* s3c24xx_serial_init_port
894 * 988 *
895 * initialise a single serial port from the platform device given 989 * 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,
914 if (port->mapbase != 0) 1008 if (port->mapbase != 0)
915 return 0; 1009 return 0;
916 1010
917 if (cfg->hwport > 3) 1011 if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
918 return -EINVAL; 1012 printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
1013 cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
1014 return -ERANGE;
1015 }
919 1016
920 /* setup info for port */ 1017 /* setup info for port */
921 port->dev = &platdev->dev; 1018 port->dev = &platdev->dev;
@@ -943,18 +1040,26 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
943 1040
944 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); 1041 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
945 1042
946 port->mapbase = res->start; 1043 port->mapbase = res->start;
947 port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART); 1044 port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
948 ret = platform_get_irq(platdev, 0); 1045 ret = platform_get_irq(platdev, 0);
949 if (ret < 0) 1046 if (ret < 0)
950 port->irq = 0; 1047 port->irq = 0;
951 else 1048 else {
952 port->irq = ret; 1049 port->irq = ret;
1050 ourport->rx_irq = ret;
1051 ourport->tx_irq = ret + 1;
1052 }
1053
1054 ret = platform_get_irq(platdev, 1);
1055 if (ret > 0)
1056 ourport->tx_irq = ret;
953 1057
954 ourport->clk = clk_get(&platdev->dev, "uart"); 1058 ourport->clk = clk_get(&platdev->dev, "uart");
955 1059
956 dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n", 1060 dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
957 port->mapbase, port->membase, port->irq, port->uartclk); 1061 port->mapbase, port->membase, port->irq,
1062 ourport->rx_irq, ourport->tx_irq, port->uartclk);
958 1063
959 /* reset the fifos (and setup the uart) */ 1064 /* reset the fifos (and setup the uart) */
960 s3c24xx_serial_resetport(port, cfg); 1065 s3c24xx_serial_resetport(port, cfg);
@@ -1002,6 +1107,10 @@ int s3c24xx_serial_probe(struct platform_device *dev,
1002 if (ret < 0) 1107 if (ret < 0)
1003 printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); 1108 printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
1004 1109
1110 ret = s3c24xx_serial_cpufreq_register(ourport);
1111 if (ret < 0)
1112 dev_err(&dev->dev, "failed to add cpufreq notifier\n");
1113
1005 return 0; 1114 return 0;
1006 1115
1007 probe_err: 1116 probe_err:
@@ -1015,6 +1124,7 @@ int s3c24xx_serial_remove(struct platform_device *dev)
1015 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); 1124 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1016 1125
1017 if (port) { 1126 if (port) {
1127 s3c24xx_serial_cpufreq_deregister(to_ourport(port));
1018 device_remove_file(&dev->dev, &dev_attr_clock_source); 1128 device_remove_file(&dev->dev, &dev_attr_clock_source);
1019 uart_remove_one_port(&s3c24xx_uart_drv, port); 1129 uart_remove_one_port(&s3c24xx_uart_drv, port);
1020 } 1130 }
@@ -1219,7 +1329,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
1219 1329
1220 platdev_ptr = s3c24xx_uart_devs; 1330 platdev_ptr = s3c24xx_uart_devs;
1221 1331
1222 for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) { 1332 for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) {
1223 s3c24xx_serial_init_port(ptr, info, *platdev_ptr); 1333 s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
1224 } 1334 }
1225 1335
@@ -1240,7 +1350,7 @@ s3c24xx_serial_console_setup(struct console *co, char *options)
1240 1350
1241 /* is this a valid port */ 1351 /* is this a valid port */
1242 1352
1243 if (co->index == -1 || co->index >= NR_PORTS) 1353 if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
1244 co->index = 0; 1354 co->index = 0;
1245 1355
1246 port = &s3c24xx_serial_ports[co->index].port; 1356 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 {
33struct s3c24xx_uart_port { 33struct s3c24xx_uart_port {
34 unsigned char rx_claimed; 34 unsigned char rx_claimed;
35 unsigned char tx_claimed; 35 unsigned char tx_claimed;
36 unsigned int pm_level;
37 unsigned long baudclk_rate;
38
39 unsigned int rx_irq;
40 unsigned int tx_irq;
36 41
37 struct s3c24xx_uart_info *info; 42 struct s3c24xx_uart_info *info;
38 struct s3c24xx_uart_clksrc *clksrc; 43 struct s3c24xx_uart_clksrc *clksrc;
39 struct clk *clk; 44 struct clk *clk;
40 struct clk *baudclk; 45 struct clk *baudclk;
41 struct uart_port port; 46 struct uart_port port;
47
48#ifdef CONFIG_CPU_FREQ
49 struct notifier_block freq_transition;
50#endif
42}; 51};
43 52
44/* conversion functions */ 53/* conversion functions */