summaryrefslogtreecommitdiff
path: root/drivers/serial
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2008-10-21 14:06:36 +0100
committerBen Dooks <ben-linux@fluff.org>2008-12-15 21:46:05 +0000
commit305554768011707f33f437b96f999f812ba2a7e4 (patch)
tree1d27e3a796c91406549f6e4472dbe3cf07310a8b /drivers/serial
parente24b864ab3e1a5916c87e13cfdc94c1d02f0578b (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>
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/samsung.c96
-rw-r--r--drivers/serial/samsung.h6
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 @@
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <asm/irq.h>
@@ -452,6 +453,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 +664,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) {
@@ -890,6 +894,93 @@ 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) {
+ printk(KERN_WARNING "%s: info NULL\n", __func__);
+ goto exit;
+ }
+
+ tty = uport->info->port.tty;
+
+ if (tty == NULL) {
+ printk(KERN_WARNING "%s: tty is NULL\n", __func__);
+ 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
@@ -1002,6 +1093,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 +1110,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);
}
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 {
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
+ unsigned int pm_level;
+ unsigned long baudclk_rate;
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 */