[PATCH] s3c2412 serial : more accurate baudrate generation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



use UDIVSLOT register on 2412, to get better accuracy when computing the
baudrate.

The clock generation is divised on 16 slots.
UDIVSLOT : if the bit n is set to 1, then for the slot n the clock generator 
divide clock source by (UBRDIV + 2) instead of (UBRDIV + 1).

So if m is the number of bit set to one, the baudrate is
CLOCK / (m*(UBRDIV + 2) + (16-m)*(UBRDIV + 1))
CLOCK / (16*(UBRDIV + 1) + m)

See 2412 datasheet for more info.

Signed-off-by: Matthieu Castet <matthieu.castet@xxxxxxxxxx>
Index: linux-2.6/drivers/serial/s3c2410.c
===================================================================
--- linux-2.6.orig/drivers/serial/s3c2410.c	2008-01-02 16:24:20.000000000 +0100
+++ linux-2.6/drivers/serial/s3c2410.c	2008-01-02 18:43:38.000000000 +0100
@@ -625,6 +625,7 @@
 	struct s3c24xx_uart_clksrc	*clksrc;
 	unsigned int			 calc;
 	unsigned int			 quot;
+	unsigned int			 slot;
 	struct clk			*src;
 };
 
@@ -643,8 +644,21 @@
 	rate /= clksrc->divisor;
 
 	calc->clksrc = clksrc;
-	calc->quot = (rate + (8 * baud)) / (16 * baud);
-	calc->calc = (rate / (calc->quot * 16));
+	if (port->type == PORT_S3C2412) {
+		unsigned long divisor;
+		unsigned long rem;
+		divisor = (rate + baud / 2) / baud;
+		calc->quot = divisor / 16; /* interger part */
+		rem = divisor % 16; /* remaining part */
+		/* slot contains rem bit(s) set to 1 */
+		calc->slot = (0xffff << (16 - rem)) & 0xffff;
+
+		calc->calc = rate / divisor;
+	}
+	else {
+		calc->quot = (rate + (8 * baud)) / (16 * baud);
+		calc->calc = (rate / (calc->quot * 16));
+	}
 
 	calc->quot--;
 	return 1;
@@ -652,7 +666,7 @@
 
 static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
 					  struct s3c24xx_uart_clksrc **clksrc,
-					  struct clk **clk,
+					  struct clk **clk, unsigned int *slot,
 					  unsigned int baud)
 {
 	struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
@@ -733,6 +747,7 @@
 
 	*clksrc = best->clksrc;
 	*clk    = best->src;
+	*slot   = best->slot;
 
 	return best->quot;
 }
@@ -746,7 +761,7 @@
 	struct s3c24xx_uart_clksrc *clksrc = NULL;
 	struct clk *clk = NULL;
 	unsigned long flags;
-	unsigned int baud, quot;
+	unsigned int baud, quot, slot = 0;
 	unsigned int ulcon;
 	unsigned int umcon;
 
@@ -765,7 +780,7 @@
 	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
 		quot = port->custom_divisor;
 	else
-		quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
+		quot = s3c24xx_serial_getclk(port, &clksrc, &clk, &slot, baud);
 
 	/* check to see if we need  to change clock source */
 
@@ -826,6 +841,8 @@
 
 	wr_regl(port, S3C2410_ULCON, ulcon);
 	wr_regl(port, S3C2410_UBRDIV, quot);
+	if (port->type == PORT_S3C2412)
+		wr_regl(port, S3C2412_UDIVSLOT, slot);
 	wr_regl(port, S3C2410_UMCON, umcon);
 
 	dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
Index: linux-2.6/include/asm-arm/plat-s3c/regs-serial.h
===================================================================
--- linux-2.6.orig/include/asm-arm/plat-s3c/regs-serial.h	2008-01-02 16:44:26.000000000 +0100
+++ linux-2.6/include/asm-arm/plat-s3c/regs-serial.h	2008-01-02 16:50:57.000000000 +0100
@@ -49,6 +49,7 @@
 #define S3C2410_UFCON	  (0x08)
 #define S3C2410_UMCON	  (0x0C)
 #define S3C2410_UBRDIV	  (0x28)
+#define S3C2412_UDIVSLOT  (0x2C)
 #define S3C2410_UTRSTAT	  (0x10)
 #define S3C2410_UERSTAT	  (0x14)
 #define S3C2410_UFSTAT	  (0x18)

[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux