aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial/samsung.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/serial/samsung.c')
-rw-r--r--drivers/serial/samsung.c184
1 files changed, 147 insertions, 37 deletions
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;