diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-10-21 09:06:36 -0400 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2008-12-15 16:46:05 -0500 |
commit | 305554768011707f33f437b96f999f812ba2a7e4 (patch) | |
tree | 1d27e3a796c91406549f6e4472dbe3cf07310a8b | |
parent | e24b864ab3e1a5916c87e13cfdc94c1d02f0578b (diff) |
[ARM] CPUFREQ: S3C24XX serial CPU frequency scaling support.
Add support for CPU frequency scalling to the S3C24XX serial
driver.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
-rw-r--r-- | drivers/serial/samsung.c | 96 | ||||
-rw-r--r-- | drivers/serial/samsung.h | 6 |
2 files changed, 102 insertions, 0 deletions
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 1e219d3d0352..ebeda832c8a3 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c | |||
@@ -42,6 +42,7 @@ | |||
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 | ||
@@ -452,6 +453,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, | |||
452 | { | 453 | { |
453 | struct s3c24xx_uart_port *ourport = to_ourport(port); | 454 | struct s3c24xx_uart_port *ourport = to_ourport(port); |
454 | 455 | ||
456 | ourport->pm_level = level; | ||
457 | |||
455 | switch (level) { | 458 | switch (level) { |
456 | case 3: | 459 | case 3: |
457 | if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) | 460 | if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) |
@@ -661,6 +664,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, | |||
661 | 664 | ||
662 | ourport->clksrc = clksrc; | 665 | ourport->clksrc = clksrc; |
663 | ourport->baudclk = clk; | 666 | ourport->baudclk = clk; |
667 | ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; | ||
664 | } | 668 | } |
665 | 669 | ||
666 | switch (termios->c_cflag & CSIZE) { | 670 | switch (termios->c_cflag & CSIZE) { |
@@ -890,6 +894,93 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port, | |||
890 | return (info->reset_port)(port, cfg); | 894 | return (info->reset_port)(port, cfg); |
891 | } | 895 | } |
892 | 896 | ||
897 | |||
898 | #ifdef CONFIG_CPU_FREQ | ||
899 | |||
900 | static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, | ||
901 | unsigned long val, void *data) | ||
902 | { | ||
903 | struct s3c24xx_uart_port *port; | ||
904 | struct uart_port *uport; | ||
905 | |||
906 | port = container_of(nb, struct s3c24xx_uart_port, freq_transition); | ||
907 | uport = &port->port; | ||
908 | |||
909 | /* check to see if port is enabled */ | ||
910 | |||
911 | if (port->pm_level != 0) | ||
912 | return 0; | ||
913 | |||
914 | /* try and work out if the baudrate is changing, we can detect | ||
915 | * a change in rate, but we do not have support for detecting | ||
916 | * a disturbance in the clock-rate over the change. | ||
917 | */ | ||
918 | |||
919 | if (IS_ERR(port->clk)) | ||
920 | goto exit; | ||
921 | |||
922 | if (port->baudclk_rate == clk_get_rate(port->clk)) | ||
923 | goto exit; | ||
924 | |||
925 | if (val == CPUFREQ_PRECHANGE) { | ||
926 | /* we should really shut the port down whilst the | ||
927 | * frequency change is in progress. */ | ||
928 | |||
929 | } else if (val == CPUFREQ_POSTCHANGE) { | ||
930 | struct ktermios *termios; | ||
931 | struct tty_struct *tty; | ||
932 | |||
933 | if (uport->info == NULL) { | ||
934 | printk(KERN_WARNING "%s: info NULL\n", __func__); | ||
935 | goto exit; | ||
936 | } | ||
937 | |||
938 | tty = uport->info->port.tty; | ||
939 | |||
940 | if (tty == NULL) { | ||
941 | printk(KERN_WARNING "%s: tty is NULL\n", __func__); | ||
942 | goto exit; | ||
943 | } | ||
944 | |||
945 | termios = tty->termios; | ||
946 | |||
947 | if (termios == NULL) { | ||
948 | printk(KERN_WARNING "%s: no termios?\n", __func__); | ||
949 | goto exit; | ||
950 | } | ||
951 | |||
952 | s3c24xx_serial_set_termios(uport, termios, NULL); | ||
953 | } | ||
954 | |||
955 | exit: | ||
956 | return 0; | ||
957 | } | ||
958 | |||
959 | static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) | ||
960 | { | ||
961 | port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; | ||
962 | |||
963 | return cpufreq_register_notifier(&port->freq_transition, | ||
964 | CPUFREQ_TRANSITION_NOTIFIER); | ||
965 | } | ||
966 | |||
967 | static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) | ||
968 | { | ||
969 | cpufreq_unregister_notifier(&port->freq_transition, | ||
970 | CPUFREQ_TRANSITION_NOTIFIER); | ||
971 | } | ||
972 | |||
973 | #else | ||
974 | static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) | ||
975 | { | ||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) | ||
980 | { | ||
981 | } | ||
982 | #endif | ||
983 | |||
893 | /* s3c24xx_serial_init_port | 984 | /* s3c24xx_serial_init_port |
894 | * | 985 | * |
895 | * initialise a single serial port from the platform device given | 986 | * initialise a single serial port from the platform device given |
@@ -1002,6 +1093,10 @@ int s3c24xx_serial_probe(struct platform_device *dev, | |||
1002 | if (ret < 0) | 1093 | if (ret < 0) |
1003 | printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); | 1094 | printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); |
1004 | 1095 | ||
1096 | ret = s3c24xx_serial_cpufreq_register(ourport); | ||
1097 | if (ret < 0) | ||
1098 | dev_err(&dev->dev, "failed to add cpufreq notifier\n"); | ||
1099 | |||
1005 | return 0; | 1100 | return 0; |
1006 | 1101 | ||
1007 | probe_err: | 1102 | probe_err: |
@@ -1015,6 +1110,7 @@ int s3c24xx_serial_remove(struct platform_device *dev) | |||
1015 | struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); | 1110 | struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); |
1016 | 1111 | ||
1017 | if (port) { | 1112 | if (port) { |
1113 | s3c24xx_serial_cpufreq_deregister(to_ourport(port)); | ||
1018 | device_remove_file(&dev->dev, &dev_attr_clock_source); | 1114 | device_remove_file(&dev->dev, &dev_attr_clock_source); |
1019 | uart_remove_one_port(&s3c24xx_uart_drv, port); | 1115 | uart_remove_one_port(&s3c24xx_uart_drv, port); |
1020 | } | 1116 | } |
diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index 5c92ebbe7d9e..be263423205d 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h | |||
@@ -33,12 +33,18 @@ struct s3c24xx_uart_info { | |||
33 | struct s3c24xx_uart_port { | 33 | struct 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; | ||
36 | 38 | ||
37 | struct s3c24xx_uart_info *info; | 39 | struct s3c24xx_uart_info *info; |
38 | struct s3c24xx_uart_clksrc *clksrc; | 40 | struct s3c24xx_uart_clksrc *clksrc; |
39 | struct clk *clk; | 41 | struct clk *clk; |
40 | struct clk *baudclk; | 42 | struct clk *baudclk; |
41 | struct uart_port port; | 43 | struct uart_port port; |
44 | |||
45 | #ifdef CONFIG_CPU_FREQ | ||
46 | struct notifier_block freq_transition; | ||
47 | #endif | ||
42 | }; | 48 | }; |
43 | 49 | ||
44 | /* conversion functions */ | 50 | /* conversion functions */ |