diff options
Diffstat (limited to 'drivers/tty/serial/sh-sci.c')
| -rw-r--r-- | drivers/tty/serial/sh-sci.c | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 63a06ab6ba03..51c7507b0444 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -38,6 +38,7 @@ #include <linux/major.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/notifier.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -115,6 +116,8 @@ struct sci_port { struct timer_list rx_timer; unsigned int rx_timeout; #endif + + struct notifier_block freq_transition; }; #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS @@ -1603,6 +1606,29 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) return ret; } +/* + * Here we define a transition notifier so that we can update all of our + * ports' baud rate when the peripheral clock changes. + */ +static int sci_notifier(struct notifier_block *self, + unsigned long phase, void *p) +{ + struct sci_port *sci_port; + unsigned long flags; + + sci_port = container_of(self, struct sci_port, freq_transition); + + if (phase == CPUFREQ_POSTCHANGE) { + struct uart_port *port = &sci_port->port; + + spin_lock_irqsave(&port->lock, flags); + port->uartclk = clk_get_rate(sci_port->iclk); + spin_unlock_irqrestore(&port->lock, flags); + } + + return NOTIFY_OK; +} + static const struct sci_irq_desc { const char *desc; irq_handler_t handler; @@ -2533,6 +2559,9 @@ static int sci_remove(struct platform_device *dev) { struct sci_port *port = platform_get_drvdata(dev); + cpufreq_unregister_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); + uart_remove_one_port(&sci_uart_driver, &port->port); sci_cleanup_single(port); @@ -2685,6 +2714,16 @@ static int sci_probe(struct platform_device *dev) if (ret) return ret; + sp->freq_transition.notifier_call = sci_notifier; + + ret = cpufreq_register_notifier(&sp->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); + if (unlikely(ret < 0)) { + uart_remove_one_port(&sci_uart_driver, &sp->port); + sci_cleanup_single(sp); + return ret; + } + #ifdef CONFIG_SH_STANDARD_BIOS sh_bios_gdb_detach(); #endif |
