All of lore.kernel.org
 help / color / mirror / Atom feed
From: Miquel Raynal <miquel.raynal@free-electrons.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Linus Walleij <linus.walleij@linaro.org>,
	Jason Cooper <jason@lakedaemon.net>, Andrew Lunn <andrew@lunn.ch>,
	Gregory Clement <gregory.clement@free-electrons.com>,
	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>,
	Jiri Slaby <jslaby@suse.com>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Will Deacon <will.deacon@arm.com>
Cc: linux-serial@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, linux-gpio@vger.kernel.org,
	Thomas Petazzoni <thomas.petazzoni@free-electrons.com>,
	Antoine Tenart <antoine.tenart@free-electrons.com>,
	Nadav Haklai <nadavh@marvell.com>,
	Wilson Ding <dingwei@marvell.com>, Allen Yan <yanwei@marvell.com>,
	Miquel Raynal <miquel.raynal@free-electrons.com>
Subject: [PATCH 07/16] serial: mvebu-uart: add function to change baudrate
Date: Fri,  6 Oct 2017 12:13:35 +0200	[thread overview]
Message-ID: <20171006101344.15590-8-miquel.raynal@free-electrons.com> (raw)
In-Reply-To: <20171006101344.15590-1-miquel.raynal@free-electrons.com>

From: Allen Yan <yanwei@marvell.com>

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 <yanwei@marvell.com>
[<miquel.raynal@free-electrons.com>: changed termios handling]
Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
---
 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


WARNING: multiple messages have this Message-ID (diff)
From: miquel.raynal@free-electrons.com (Miquel Raynal)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 07/16] serial: mvebu-uart: add function to change baudrate
Date: Fri,  6 Oct 2017 12:13:35 +0200	[thread overview]
Message-ID: <20171006101344.15590-8-miquel.raynal@free-electrons.com> (raw)
In-Reply-To: <20171006101344.15590-1-miquel.raynal@free-electrons.com>

From: Allen Yan <yanwei@marvell.com>

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 <yanwei@marvell.com>
[<miquel.raynal@free-electrons.com>: changed termios handling]
Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
---
 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

  parent reply	other threads:[~2017-10-06 10:14 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-06 10:13 [PATCH 00/16] Support armada-37xx second UART port Miquel Raynal
2017-10-06 10:13 ` Miquel Raynal
2017-10-06 10:13 ` [PATCH 04/16] serial: mvebu-uart: support probe of multiple ports Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
     [not found]   ` <20171006101344.15590-5-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 12:23     ` Gregory CLEMENT
2017-10-06 12:23       ` Gregory CLEMENT
     [not found]       ` <87poa0foro.fsf-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-09  7:17         ` Miquel RAYNAL
2017-10-09  7:17           ` Miquel RAYNAL
2017-10-12 12:22           ` Gregory CLEMENT
2017-10-12 12:22             ` Gregory CLEMENT
     [not found]             ` <87vajkblol.fsf-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-13  7:29               ` Miquel RAYNAL
2017-10-13  7:29                 ` Miquel RAYNAL
2017-10-06 10:13 ` [PATCH 06/16] serial: mvebu-uart: add soft reset at probe Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
2017-10-06 12:33   ` Gregory CLEMENT
2017-10-06 12:33     ` Gregory CLEMENT
2017-10-06 10:13 ` Miquel Raynal [this message]
2017-10-06 10:13   ` [PATCH 07/16] serial: mvebu-uart: add function to change baudrate Miquel Raynal
2017-10-06 12:39   ` Gregory CLEMENT
2017-10-06 12:39     ` Gregory CLEMENT
2017-10-06 10:13 ` [PATCH 09/16] serial: mvebu-uart: add TX interrupt trigger for pulse interrupts Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
     [not found]   ` <20171006101344.15590-10-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 20:22     ` Gregory CLEMENT
2017-10-06 20:22       ` Gregory CLEMENT
2017-10-06 10:13 ` [PATCH 10/16] serial: mvebu-uart: dissociate RX and TX interrupts Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
2017-10-06 13:11   ` Gregory CLEMENT
2017-10-06 13:11     ` Gregory CLEMENT
2017-10-06 10:13 ` [PATCH 11/16] serial: mvebu-uart: augment the maximum number of ports Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
2017-10-06 12:45   ` Gregory CLEMENT
2017-10-06 12:45     ` Gregory CLEMENT
     [not found] ` <20171006101344.15590-1-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 10:13   ` [PATCH 01/16] dt-bindings: mvebu-uart: update documentation with extended UART Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
2017-10-06 12:17     ` Gregory CLEMENT
2017-10-06 12:17       ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 02/16] pinctrl: dt-bindings: Fix A37xx uart2 group name Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
     [not found]     ` <20171006101344.15590-3-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 12:18       ` Gregory CLEMENT
2017-10-06 12:18         ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 03/16] serial: mvebu-uart: use driver name when requesting an interrupt Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
     [not found]     ` <20171006101344.15590-4-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 12:19       ` Gregory CLEMENT
2017-10-06 12:19         ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 05/16] serial: mvebu-uart: use a generic way to access the registers Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
2017-10-06 12:32     ` Gregory CLEMENT
2017-10-06 12:32       ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 08/16] serial: mvebu-uart: clear state register before IRQ request Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
     [not found]     ` <20171006101344.15590-9-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 12:40       ` Gregory CLEMENT
2017-10-06 12:40         ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 12/16] serial: mvebu-uart: support extended port registers layout Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
2017-10-06 12:46     ` Gregory CLEMENT
2017-10-06 12:46       ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 13/16] arm64: dts: marvell: armada-37xx: add UART clock Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
     [not found]     ` <20171006101344.15590-14-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 12:48       ` Gregory CLEMENT
2017-10-06 12:48         ` Gregory CLEMENT
2017-10-06 10:13   ` [PATCH 14/16] arm64: dts: marvell: armada-37xx: add second UART port Miquel Raynal
2017-10-06 10:13     ` Miquel Raynal
2017-10-06 12:49     ` Gregory CLEMENT
2017-10-06 12:49       ` Gregory CLEMENT
2017-10-06 10:13 ` [PATCH 15/16] arm64: dts: marvell: armada-3720-db: enable " Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
     [not found]   ` <20171006101344.15590-16-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 12:51     ` Gregory CLEMENT
2017-10-06 12:51       ` Gregory CLEMENT
2017-10-06 10:13 ` [PATCH 16/16] arm64: dts: marvell: armada-3720-espressobin: fill UART nodes Miquel Raynal
2017-10-06 10:13   ` Miquel Raynal
     [not found]   ` <20171006101344.15590-17-miquel.raynal-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 13:01     ` Gregory CLEMENT
2017-10-06 13:01       ` Gregory CLEMENT
     [not found]       ` <87fuawe8gx.fsf-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-06 13:15         ` Thomas Petazzoni
2017-10-06 13:15           ` Thomas Petazzoni
2017-10-09  7:30           ` Miquel RAYNAL
2017-10-09  7:30             ` Miquel RAYNAL
2017-10-12 11:24             ` Gregory CLEMENT
2017-10-12 11:24               ` Gregory CLEMENT
     [not found]               ` <87zi8wbocl.fsf-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2017-10-13  7:01                 ` Miquel RAYNAL
2017-10-13  7:01                   ` Miquel RAYNAL

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171006101344.15590-8-miquel.raynal@free-electrons.com \
    --to=miquel.raynal@free-electrons.com \
    --cc=andrew@lunn.ch \
    --cc=antoine.tenart@free-electrons.com \
    --cc=catalin.marinas@arm.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dingwei@marvell.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=gregory.clement@free-electrons.com \
    --cc=jason@lakedaemon.net \
    --cc=jslaby@suse.com \
    --cc=linus.walleij@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=nadavh@marvell.com \
    --cc=sebastian.hesselbarth@gmail.com \
    --cc=thomas.petazzoni@free-electrons.com \
    --cc=will.deacon@arm.com \
    --cc=yanwei@marvell.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.