@@ -38,6 +38,8 @@
#include <linux/serial_8250.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -156,6 +158,10 @@ struct uart_8250_port {
*/
void (*pm)(struct uart_port *port,
unsigned int state, unsigned int old);
+ struct clk *uart_clk;
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
};
struct irq_info {
@@ -2931,6 +2937,70 @@ void serial8250_resume_port(int line)
uart_resume_port(&serial8250_reg, &up->port);
}
+#ifdef CONFIG_CPU_FREQ
+static int serial8250_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct uart_8250_port *p;
+ struct uart_port *uport;
+
+ p = container_of(nb, struct uart_8250_port, freq_transition);
+ uport = &p->port;
+
+ if(!p->port.uartclk)
+ goto cpu_freq_exit;
+
+ if (p->port.uartclk == clk_get_rate(p->uart_clk))
+ goto cpu_freq_exit;
+
+ p->port.uartclk = clk_get_rate(p->uart_clk);
+ if (val == CPUFREQ_POSTCHANGE) {
+ struct ktermios *termios;
+ struct tty_struct *tty;
+ if (uport->state == NULL)
+ goto cpu_freq_exit;
+
+ tty = uport->state->port.tty;
+ if (tty == NULL)
+ goto cpu_freq_exit;
+
+ termios = tty->termios;
+ if (termios == NULL) {
+ printk(KERN_WARNING "%s: no termios?\n", __func__);
+ goto cpu_freq_exit;
+ }
+
+ serial8250_set_termios(uport, termios, NULL);
+ }
+
+cpu_freq_exit:
+ return 0;
+}
+
+static inline int serial8250_cpufreq_register(struct uart_8250_port *p)
+{
+ p->freq_transition.notifier_call = serial8250_cpufreq_transition;
+
+ return cpufreq_register_notifier(&p->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void serial8250_cpufreq_deregister(struct uart_8250_port *p)
+{
+ cpufreq_unregister_notifier(&p->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+#else
+static inline int serial8250_cpufreq_register(struct uart_8250_port *p)
+{
+ return 0;
+}
+
+static inline void serial8250_cpufreq_deregister(struct uart_8250_port *p)
+{
+}
+#endif
+
/*
* Register a set of serial devices attached to a platform device. The
* list is terminated with a zero flags entry, which means we expect
@@ -3090,6 +3160,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
*/
int serial8250_register_port(struct uart_port *port)
{
+ struct plat_serial8250_port *p;
struct uart_8250_port *uart;
int ret = -ENOSPC;
@@ -3116,6 +3187,10 @@ int serial8250_register_port(struct uart_port *port)
if (port->dev)
uart->port.dev = port->dev;
+ p = port->dev->platform_data;
+ if (p->uart_clk)
+ uart->uart_clk = p->uart_clk;
+
if (port->flags & UPF_FIXED_TYPE) {
uart->port.type = port->type;
uart->port.fifosize = uart_config[port->type].fifo_size;
@@ -3133,6 +3208,11 @@ int serial8250_register_port(struct uart_port *port)
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
ret = uart->port.line;
+
+ ret = serial8250_cpufreq_register(uart);
+ if (ret < 0)
+ printk(KERN_ERR "Failed to add cpufreq notifier\n");
+
}
mutex_unlock(&serial_mutex);
@@ -3152,6 +3232,7 @@ void serial8250_unregister_port(int line)
struct uart_8250_port *uart = &serial8250_ports[line];
mutex_lock(&serial_mutex);
+ serial8250_cpufreq_deregister(uart);
uart_remove_one_port(&serial8250_reg, &uart->port);
if (serial8250_isa_devs) {
uart->port.flags &= ~UPF_BOOT_AUTOCONF;
@@ -23,6 +23,7 @@ struct plat_serial8250_port {
resource_size_t mapbase; /* resource base */
unsigned int irq; /* interrupt number */
unsigned long irqflags; /* request_irq flags */
+ struct clk *uart_clk;
unsigned int uartclk; /* UART clock rate */
void *private_data;
unsigned char regshift; /* register shift */