diff mbox

[RFC] serial: 8250: Add cpufreq support

Message ID 1262955698-6130-1-git-send-email-chaithrika@ti.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

chaithrika@ti.com Jan. 8, 2010, 1:01 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index c3e37c8..612e129 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -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;
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index fb46aba..d85201f 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -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 */