Thread (7 messages) 7 messages, 3 authors, 2016-09-26

Re: [PATCH] tty/serial: atmel: fix fractional baud rate computation

From: Boris Brezillon <hidden>
Date: 2016-09-22 07:39:21
Also in: linux-arm-kernel, lkml

On Thu, 22 Sep 2016 09:07:46 +0200
Uwe Kleine-König [off-list ref] wrote:
On Wed, Sep 21, 2016 at 12:44:14PM +0200, Nicolas Ferre wrote:
quoted
From: Alexey Starikovskiy <redacted>

The problem with previous code was it rounded values in wrong
place and produced wrong baud rate in some cases.

Signed-off-by: Alexey Starikovskiy <redacted>
[nicolas.ferre@atmel.com: port to newer kernel and add commit log]
Signed-off-by: Nicolas Ferre <redacted>
---
 drivers/tty/serial/atmel_serial.c | 10 ++++++----
 include/linux/atmel_serial.h      |  1 +
 2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 5f550d9feed9..fd8aa1f4ba78 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2170,13 +2170,15 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
 	 * accurately. This feature is enabled only when using normal mode.
 	 * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8))
 	 * Currently, OVER is always set to 0 so we get
-	 * baudrate = selected clock (16 * (CD + FP / 8))
+	 * baudrate = selected clock / (16 * (CD + FP / 8))
+	 * then
+	 * 8 CD + FP = selected clock / (2 * baudrate)
 	 */
 	if (atmel_port->has_frac_baudrate &&
 	    (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) {
-		div = DIV_ROUND_CLOSEST(port->uartclk, baud);
-		cd = div / 16;
-		fp = DIV_ROUND_CLOSEST(div % 16, 2);
+		div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2);
+		cd = div >> 3;
+		fp = div & ATMEL_US_FP_MASK;  
given baud = 115200 and uartclk = 5414300 this results in:

	div = DIV_ROUND_CLOSEST(5414300, 115200 * 2) = 23
	cd = 2
	fp = 7
How about:

	div = DIV_ROUND_CLOSEST(port->uartclk, baud);
	cd = div / 16;
	fp = (div % 16) / 2;

	best_baud = port->uartclk / ((16 * cd) +  (8 * fp));

	/* Check if we can get a better approximation by rounding up. */
	if (div % 2) {
		int alt_baud, alt_fp, alt_cd;

		alt_fp = fp++;
		alt_cd = cd;
		if (alt_fp > 7) {
			alt_cd++;
			alt_fp = 0;
		}

		alt_baud = port->uartclk / ((16 * alt_cd) +  (8 *alt_fp));
		if (abs(best_baud - baud) > abs(alt_baud - baud)) {
			best_baud = alt_baud;
			fp = alt_fp;
			cd = alt_cd;
		}
	}
which yields a rate of 5414300 / 46 = 117702.17. With cd = 3 and fp = 0
however the resulting rate is 5414300 / 48 = 112797.92.

Which one is better?
quoted
 	} else {
 		cd = uart_get_divisor(port, baud);
 	}
diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h
index f8e452aa48d7..bd2560502f3c 100644
--- a/include/linux/atmel_serial.h
+++ b/include/linux/atmel_serial.h
@@ -119,6 +119,7 @@
 #define ATMEL_US_BRGR		0x20	/* Baud Rate Generator Register */
 #define	ATMEL_US_CD		GENMASK(15, 0)	/* Clock Divider */
 #define ATMEL_US_FP_OFFSET	16	/* Fractional Part */
+#define ATMEL_US_FP_MASK	0x7  
Is there another user of this header? If not, this can be folded into
the driver.

Best regards
Uwe
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help