Commit 30555476 authored by Ben Dooks's avatar Ben Dooks

[ARM] CPUFREQ: S3C24XX serial CPU frequency scaling support.

Add support for CPU frequency scalling to the S3C24XX serial
driver.
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
parent e24b864a
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpufreq.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -452,6 +453,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, ...@@ -452,6 +453,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
{ {
struct s3c24xx_uart_port *ourport = to_ourport(port); struct s3c24xx_uart_port *ourport = to_ourport(port);
ourport->pm_level = level;
switch (level) { switch (level) {
case 3: case 3:
if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
...@@ -661,6 +664,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ...@@ -661,6 +664,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
ourport->clksrc = clksrc; ourport->clksrc = clksrc;
ourport->baudclk = clk; ourport->baudclk = clk;
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
} }
switch (termios->c_cflag & CSIZE) { switch (termios->c_cflag & CSIZE) {
...@@ -890,6 +894,93 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port, ...@@ -890,6 +894,93 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port,
return (info->reset_port)(port, cfg); 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 /* s3c24xx_serial_init_port
* *
* initialise a single serial port from the platform device given * initialise a single serial port from the platform device given
...@@ -1002,6 +1093,10 @@ int s3c24xx_serial_probe(struct platform_device *dev, ...@@ -1002,6 +1093,10 @@ int s3c24xx_serial_probe(struct platform_device *dev,
if (ret < 0) if (ret < 0)
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); 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; return 0;
probe_err: probe_err:
...@@ -1015,6 +1110,7 @@ int s3c24xx_serial_remove(struct platform_device *dev) ...@@ -1015,6 +1110,7 @@ int s3c24xx_serial_remove(struct platform_device *dev)
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
if (port) { if (port) {
s3c24xx_serial_cpufreq_deregister(to_ourport(port));
device_remove_file(&dev->dev, &dev_attr_clock_source); device_remove_file(&dev->dev, &dev_attr_clock_source);
uart_remove_one_port(&s3c24xx_uart_drv, port); uart_remove_one_port(&s3c24xx_uart_drv, port);
} }
......
...@@ -33,12 +33,18 @@ struct s3c24xx_uart_info { ...@@ -33,12 +33,18 @@ struct s3c24xx_uart_info {
struct s3c24xx_uart_port { struct s3c24xx_uart_port {
unsigned char rx_claimed; unsigned char rx_claimed;
unsigned char tx_claimed; unsigned char tx_claimed;
unsigned int pm_level;
unsigned long baudclk_rate;
struct s3c24xx_uart_info *info; struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc; struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk; struct clk *clk;
struct clk *baudclk; struct clk *baudclk;
struct uart_port port; struct uart_port port;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
}; };
/* conversion functions */ /* conversion functions */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment