summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/sh-sci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/sh-sci.c')
-rw-r--r--drivers/tty/serial/sh-sci.c39
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