From mboxrd@z Thu Jan 1 00:00:00 1970 From: Gregory CLEMENT Subject: Re: [PATCH 07/16] serial: mvebu-uart: add function to change baudrate Date: Fri, 06 Oct 2017 14:39:35 +0200 Message-ID: <87d160fo1k.fsf@free-electrons.com> References: <20171006101344.15590-1-miquel.raynal@free-electrons.com> <20171006101344.15590-8-miquel.raynal@free-electrons.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: Received: from mail.free-electrons.com ([62.4.15.54]:56666 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751961AbdJFMjr (ORCPT ); Fri, 6 Oct 2017 08:39:47 -0400 In-Reply-To: <20171006101344.15590-8-miquel.raynal@free-electrons.com> (Miquel Raynal's message of "Fri, 6 Oct 2017 12:13:35 +0200") Sender: linux-gpio-owner@vger.kernel.org List-Id: linux-gpio@vger.kernel.org To: Miquel Raynal Cc: Greg Kroah-Hartman , Linus Walleij , Jason Cooper , Andrew Lunn , Sebastian Hesselbarth , Jiri Slaby , Catalin Marinas , Will Deacon , Thomas Petazzoni , devicetree@vger.kernel.org, Allen Yan , Antoine Tenart , Nadav Haklai , linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, Wilson Ding , linux-arm-kernel@lists.infradead.org Hi Miquel, On ven., oct. 06 2017, Miquel Raynal wrote: > From: Allen Yan > > Until now, the first UART port baudrate was set by the bootloader. > > Add a function allowing to change the baudrate. Changes may be done > from userspace but also at probe time by the kernel. Use the simplest > method: baudrate divisor. > > Works for all UART ports until 230400 baud. To achieve higher baudrates, > software should implement the fractional divisor feature that allows > more accuracy for higher rates. > > Signed-off-by: Allen Yan > [: changed termios handling] > Signed-off-by: Miquel Raynal Reviewed-by: Gregory CLEMENT Thanks, Gregory > --- > drivers/tty/serial/mvebu-uart.c | 69 ++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 65 insertions(+), 4 deletions(-) > > diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c > index da756cfec0bb..81a3d2714fd3 100644 > --- a/drivers/tty/serial/mvebu-uart.c > +++ b/drivers/tty/serial/mvebu-uart.c > @@ -72,6 +72,7 @@ > | STAT_PAR_ERR | STAT_OVR_ERR) > > #define UART_BRDV 0x10 > +#define BRDV_BAUD_MASK 0x3FF > > #define MVEBU_NR_UARTS 1 > > @@ -344,6 +345,31 @@ static void mvebu_uart_shutdown(struct uart_port *port) > free_irq(port->irq, port); > } > > +static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) > +{ > + struct mvebu_uart *mvuart = to_mvuart(port); > + unsigned int baud_rate_div; > + u32 brdv; > + > + if (IS_ERR(mvuart->clk)) > + return -PTR_ERR(mvuart->clk); > + > + /* > + * The UART clock is divided by the value of the divisor to generate > + * UCLK_OUT clock, which is 16 times faster than the baudrate. > + * This prescaler can achieve all standard baudrates until 230400. > + * Higher baudrates could be achieved for the extended UART by using the > + * programmable oversampling stack (also called fractional divisor). > + */ > + baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16); > + brdv = readl(port->membase + UART_BRDV); > + brdv &= ~BRDV_BAUD_MASK; > + brdv |= baud_rate_div; > + writel(brdv, port->membase + UART_BRDV); > + > + return 0; > +} > + > static void mvebu_uart_set_termios(struct uart_port *port, > struct ktermios *termios, > struct ktermios *old) > @@ -367,11 +393,30 @@ static void mvebu_uart_set_termios(struct uart_port *port, > if ((termios->c_cflag & CREAD) == 0) > port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR; > > - if (old) > - tty_termios_copy_hw(termios, old); > + /* > + * Maximum achievable frequency with simple baudrate divisor is 230400. > + * Since the error per bit frame would be of more than 15%, achieving > + * higher frequencies would require to implement the fractional divisor > + * feature. > + */ > + baud = uart_get_baud_rate(port, termios, old, 0, 230400); > + if (mvebu_uart_baud_rate_set(port, baud)) { > + /* No clock available, baudrate cannot be changed */ > + if (old) > + baud = uart_get_baud_rate(port, old, NULL, 0, 230400); > + } else { > + tty_termios_encode_baud_rate(termios, baud, baud); > + uart_update_timeout(port, termios->c_cflag, baud); > + } > > - baud = uart_get_baud_rate(port, termios, old, 0, 460800); > - uart_update_timeout(port, termios->c_cflag, baud); > + /* Only the following flag changes are supported */ > + if (old) { > + termios->c_iflag &= INPCK | IGNPAR; > + termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR); > + termios->c_cflag &= CREAD | CBAUD; > + termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD); > + termios->c_lflag = old->c_lflag; > + } > > spin_unlock_irqrestore(&port->lock, flags); > } > @@ -647,12 +692,28 @@ static int mvebu_uart_probe(struct platform_device *pdev) > if (!mvuart) > return -ENOMEM; > > + /* Get controller data depending on the compatible string */ > mvuart->data = (struct mvebu_uart_driver_data *)match->data; > mvuart->port = port; > > port->private_data = mvuart; > platform_set_drvdata(pdev, mvuart); > > + /* Get fixed clock frequency */ > + mvuart->clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(mvuart->clk)) { > + if (PTR_ERR(mvuart->clk) == -EPROBE_DEFER) > + return PTR_ERR(mvuart->clk); > + > + if (IS_EXTENDED(port)) { > + dev_err(&pdev->dev, "unable to get UART clock\n"); > + return PTR_ERR(mvuart->clk); > + } > + } else { > + if (!clk_prepare_enable(mvuart->clk)) > + port->uartclk = clk_get_rate(mvuart->clk); > + } > + > /* UART Soft Reset*/ > writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port)); > udelay(1); > -- > 2.11.0 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- Gregory Clement, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com From mboxrd@z Thu Jan 1 00:00:00 1970 From: gregory.clement@free-electrons.com (Gregory CLEMENT) Date: Fri, 06 Oct 2017 14:39:35 +0200 Subject: [PATCH 07/16] serial: mvebu-uart: add function to change baudrate In-Reply-To: <20171006101344.15590-8-miquel.raynal@free-electrons.com> (Miquel Raynal's message of "Fri, 6 Oct 2017 12:13:35 +0200") References: <20171006101344.15590-1-miquel.raynal@free-electrons.com> <20171006101344.15590-8-miquel.raynal@free-electrons.com> Message-ID: <87d160fo1k.fsf@free-electrons.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Hi Miquel, On ven., oct. 06 2017, Miquel Raynal wrote: > From: Allen Yan > > Until now, the first UART port baudrate was set by the bootloader. > > Add a function allowing to change the baudrate. Changes may be done > from userspace but also at probe time by the kernel. Use the simplest > method: baudrate divisor. > > Works for all UART ports until 230400 baud. To achieve higher baudrates, > software should implement the fractional divisor feature that allows > more accuracy for higher rates. > > Signed-off-by: Allen Yan > [: changed termios handling] > Signed-off-by: Miquel Raynal Reviewed-by: Gregory CLEMENT Thanks, Gregory > --- > drivers/tty/serial/mvebu-uart.c | 69 ++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 65 insertions(+), 4 deletions(-) > > diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c > index da756cfec0bb..81a3d2714fd3 100644 > --- a/drivers/tty/serial/mvebu-uart.c > +++ b/drivers/tty/serial/mvebu-uart.c > @@ -72,6 +72,7 @@ > | STAT_PAR_ERR | STAT_OVR_ERR) > > #define UART_BRDV 0x10 > +#define BRDV_BAUD_MASK 0x3FF > > #define MVEBU_NR_UARTS 1 > > @@ -344,6 +345,31 @@ static void mvebu_uart_shutdown(struct uart_port *port) > free_irq(port->irq, port); > } > > +static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) > +{ > + struct mvebu_uart *mvuart = to_mvuart(port); > + unsigned int baud_rate_div; > + u32 brdv; > + > + if (IS_ERR(mvuart->clk)) > + return -PTR_ERR(mvuart->clk); > + > + /* > + * The UART clock is divided by the value of the divisor to generate > + * UCLK_OUT clock, which is 16 times faster than the baudrate. > + * This prescaler can achieve all standard baudrates until 230400. > + * Higher baudrates could be achieved for the extended UART by using the > + * programmable oversampling stack (also called fractional divisor). > + */ > + baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16); > + brdv = readl(port->membase + UART_BRDV); > + brdv &= ~BRDV_BAUD_MASK; > + brdv |= baud_rate_div; > + writel(brdv, port->membase + UART_BRDV); > + > + return 0; > +} > + > static void mvebu_uart_set_termios(struct uart_port *port, > struct ktermios *termios, > struct ktermios *old) > @@ -367,11 +393,30 @@ static void mvebu_uart_set_termios(struct uart_port *port, > if ((termios->c_cflag & CREAD) == 0) > port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR; > > - if (old) > - tty_termios_copy_hw(termios, old); > + /* > + * Maximum achievable frequency with simple baudrate divisor is 230400. > + * Since the error per bit frame would be of more than 15%, achieving > + * higher frequencies would require to implement the fractional divisor > + * feature. > + */ > + baud = uart_get_baud_rate(port, termios, old, 0, 230400); > + if (mvebu_uart_baud_rate_set(port, baud)) { > + /* No clock available, baudrate cannot be changed */ > + if (old) > + baud = uart_get_baud_rate(port, old, NULL, 0, 230400); > + } else { > + tty_termios_encode_baud_rate(termios, baud, baud); > + uart_update_timeout(port, termios->c_cflag, baud); > + } > > - baud = uart_get_baud_rate(port, termios, old, 0, 460800); > - uart_update_timeout(port, termios->c_cflag, baud); > + /* Only the following flag changes are supported */ > + if (old) { > + termios->c_iflag &= INPCK | IGNPAR; > + termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR); > + termios->c_cflag &= CREAD | CBAUD; > + termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD); > + termios->c_lflag = old->c_lflag; > + } > > spin_unlock_irqrestore(&port->lock, flags); > } > @@ -647,12 +692,28 @@ static int mvebu_uart_probe(struct platform_device *pdev) > if (!mvuart) > return -ENOMEM; > > + /* Get controller data depending on the compatible string */ > mvuart->data = (struct mvebu_uart_driver_data *)match->data; > mvuart->port = port; > > port->private_data = mvuart; > platform_set_drvdata(pdev, mvuart); > > + /* Get fixed clock frequency */ > + mvuart->clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(mvuart->clk)) { > + if (PTR_ERR(mvuart->clk) == -EPROBE_DEFER) > + return PTR_ERR(mvuart->clk); > + > + if (IS_EXTENDED(port)) { > + dev_err(&pdev->dev, "unable to get UART clock\n"); > + return PTR_ERR(mvuart->clk); > + } > + } else { > + if (!clk_prepare_enable(mvuart->clk)) > + port->uartclk = clk_get_rate(mvuart->clk); > + } > + > /* UART Soft Reset*/ > writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port)); > udelay(1); > -- > 2.11.0 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- Gregory Clement, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com