diff options
Diffstat (limited to 'drivers/serial/samsung.c')
-rw-r--r-- | drivers/serial/samsung.c | 184 |
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 | ||
137 | static void s3c24xx_serial_stop_tx(struct uart_port *port) | 125 | static 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 | ||
147 | static void s3c24xx_serial_start_tx(struct uart_port *port) | 137 | static 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 | ||
159 | static void s3c24xx_serial_stop_rx(struct uart_port *port) | 151 | static 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 = { | |||
827 | static struct uart_driver s3c24xx_uart_drv = { | 824 | static 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 | ||
837 | static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { | 834 | static 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 | |||
907 | static 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 | |||
962 | static 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 | |||
970 | static 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 | ||
977 | static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) | ||
978 | { | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static 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; |