History

serial: 8250: Add 8250 port clock update method

Some platforms can be designed in a way so the UART port reference clock might be asynchronously changed at some point. In Baikal-T1 SoC this may happen due to the reference clock being shared between two UART ports, on the Allwinner SoC the reference clock is derived from the CPU clock, so any CPU frequency change should get to be known/reflected by/in the UART controller as well. But it's not enough to just update the uart_port->uartclk field of the corresponding UART port, the 8250 controller reference clock divisor should be altered so to preserve current baud rate setting. All of these things is done in a coherent way by calling the serial8250_update_uartclk() method provided in this patch. Though note that it isn't supposed to be called from within the UART port callbacks because the locks using to the protect the UART port data are already taken in there. Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

Serge Semin committed 6 years ago

Commit 868f3ee1 parent 699cc4d2 files · +42 −0Browse files

Unified · Split

2 files
  1. drivers/tty/serial/8250/8250_port.c +40 −0
  2. include/linux/serial_8250.h +2 −0
drivers/tty/serial/8250/8250_port.c+40−0
@@ -2632,6 +2632,46 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, · 2631 unmodified lines
2632 (port->uartclk + tolerance) / 16);2632 (port->uartclk + tolerance) / 16);
2633}2633}
2634 2634
2635/*
2636 * Note in order to avoid the tty port mutex deadlock don't use the next method
2637 * within the uart port callbacks. Primarily it's supposed to be utilized to
2638 * handle a sudden reference clock rate change.
2639 */
2640void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk)
2641{
2642 struct uart_8250_port *up = up_to_u8250p(port);
2643 unsigned int baud, quot, frac = 0;
2644 struct ktermios *termios;
2645 unsigned long flags;
2646
2647 mutex_lock(&port->state->port.mutex);
2648
2649 if (port->uartclk == uartclk)
2650 goto out_lock;
2651
2652 port->uartclk = uartclk;
2653 termios = &port->state->port.tty->termios;
2654
2655 baud = serial8250_get_baud_rate(port, termios, NULL);
2656 quot = serial8250_get_divisor(port, baud, &frac);
2657
2658 serial8250_rpm_get(up);
2659 spin_lock_irqsave(&port->lock, flags);
2660
2661 uart_update_timeout(port, termios->c_cflag, baud);
2662
2663 serial8250_set_divisor(port, baud, quot, frac);
2664 serial_port_out(port, UART_LCR, up->lcr);
2665 serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
2666
2667 spin_unlock_irqrestore(&port->lock, flags);
2668 serial8250_rpm_put(up);
2669
2670out_lock:
2671 mutex_unlock(&port->state->port.mutex);
2672}
2673EXPORT_SYMBOL_GPL(serial8250_update_uartclk);
2674
2635void2675void
2636serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,2676serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
2637 struct ktermios *old)2677 struct ktermios *old)
include/linux/serial_8250.h+2−0
@@ -155,6 +155,8 @@ extern int early_serial_setup(struct uart_port *port); · 154 unmodified lines
155 155
156extern int early_serial8250_setup(struct earlycon_device *device,156extern int early_serial8250_setup(struct earlycon_device *device,
157 const char *options);157 const char *options);
158extern void serial8250_update_uartclk(struct uart_port *port,
159 unsigned int uartclk);
158extern void serial8250_do_set_termios(struct uart_port *port,160extern void serial8250_do_set_termios(struct uart_port *port,
159 struct ktermios *termios, struct ktermios *old);161 struct ktermios *termios, struct ktermios *old);
160extern void serial8250_do_set_ldisc(struct uart_port *port,162extern void serial8250_do_set_ldisc(struct uart_port *port,