linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* imx: serial: add IrDA support to serial driver
@ 2009-06-10 12:08 Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 1/9 -v2] imx: serial: fix whitespaces (no changes in functionality) Oskar Schirmer
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer


Using the iMX serial driver with an IrDA device
needs extra peripheral settings and specific
timing depending on the transmitter circuitry used.

The first five patches fix minor problems in the
generic part of the iMX serial driver

The sixth introduces a generic function to calculate optimum
numerator denominator value pairs for PLL and clock
register settings, the seventh patch uses this function
in the iMX serial driver.

The eighth patch adds IrDA support to the iMX serial driver,
the last one sets the real baud rate thru tty_encode_baud_rate,
as suggested by Alan Cox.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/9 -v2] imx: serial: fix whitespaces (no changes in functionality)
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 2/9 -v2] imx: serial: fix one bit field type Oskar Schirmer
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer

Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/imx.c |   48 +++++++++++++++++++++++-------------------------
 1 files changed, 23 insertions(+), 25 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 9f460b1..424f679 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -302,8 +302,7 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
 		/* send xmit->buf[xmit->tail]
 		 * out the port here */
 		writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
-		xmit->tail = (xmit->tail + 1) &
-		         (UART_XMIT_SIZE - 1);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 		sport->port.icount.tx++;
 		if (uart_circ_empty(xmit))
 			break;
@@ -395,8 +394,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 				continue;
 		}
 
-		if (uart_handle_sysrq_char
-		            (&sport->port, (unsigned char)rx))
+		if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
 			continue;
 
 		if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) {
@@ -471,26 +469,26 @@ static unsigned int imx_tx_empty(struct uart_port *port)
  */
 static unsigned int imx_get_mctrl(struct uart_port *port)
 {
-        struct imx_port *sport = (struct imx_port *)port;
-        unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
 
-        if (readl(sport->port.membase + USR1) & USR1_RTSS)
-                tmp |= TIOCM_CTS;
+	if (readl(sport->port.membase + USR1) & USR1_RTSS)
+		tmp |= TIOCM_CTS;
 
-        if (readl(sport->port.membase + UCR2) & UCR2_CTS)
-                tmp |= TIOCM_RTS;
+	if (readl(sport->port.membase + UCR2) & UCR2_CTS)
+		tmp |= TIOCM_RTS;
 
-        return tmp;
+	return tmp;
 }
 
 static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
-        struct imx_port *sport = (struct imx_port *)port;
+	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
 
 	temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
 
-        if (mctrl & TIOCM_RTS)
+	if (mctrl & TIOCM_RTS)
 		temp |= UCR2_CTS;
 
 	writel(temp, sport->port.membase + UCR2);
@@ -1070,22 +1068,22 @@ static struct uart_driver imx_reg = {
 
 static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
 {
-        struct imx_port *sport = platform_get_drvdata(dev);
+	struct imx_port *sport = platform_get_drvdata(dev);
 
-        if (sport)
-                uart_suspend_port(&imx_reg, &sport->port);
+	if (sport)
+		uart_suspend_port(&imx_reg, &sport->port);
 
-        return 0;
+	return 0;
 }
 
 static int serial_imx_resume(struct platform_device *dev)
 {
-        struct imx_port *sport = platform_get_drvdata(dev);
+	struct imx_port *sport = platform_get_drvdata(dev);
 
-        if (sport)
-                uart_resume_port(&imx_reg, &sport->port);
+	if (sport)
+		uart_resume_port(&imx_reg, &sport->port);
 
-        return 0;
+	return 0;
 }
 
 static int serial_imx_probe(struct platform_device *pdev)
@@ -1141,7 +1139,7 @@ static int serial_imx_probe(struct platform_device *pdev)
 	imx_ports[pdev->id] = sport;
 
 	pdata = pdev->dev.platform_data;
-	if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
+	if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
 		sport->have_rtscts = 1;
 
 	if (pdata->init) {
@@ -1191,13 +1189,13 @@ static int serial_imx_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver serial_imx_driver = {
-        .probe          = serial_imx_probe,
-        .remove         = serial_imx_remove,
+	.probe		= serial_imx_probe,
+	.remove		= serial_imx_remove,
 
 	.suspend	= serial_imx_suspend,
 	.resume		= serial_imx_resume,
 	.driver		= {
-	        .name	= "imx-uart",
+		.name	= "imx-uart",
 		.owner	= THIS_MODULE,
 	},
 };
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/9 -v2] imx: serial: fix one bit field type
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 1/9 -v2] imx: serial: fix whitespaces (no changes in functionality) Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 3/9 -v2] imx: serial: notify higher layers in case xmit IRQ was not called Oskar Schirmer
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer,
	Daniel Glöckner

From: Daniel Glöckner <dg@emlix.com>

"have_rtscts" is assigned 1, while it is declared
int:1, two's complement, which can hold 0 and -1
only. The code works, as the upper bits are cut
off, and tests are done against 0 only.

Nonetheless, correctly declaring the bit field
as unsigned int:1 renders the code more robust.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/imx.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 424f679..5ee325f 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -211,7 +211,7 @@ struct imx_port {
 	struct timer_list	timer;
 	unsigned int		old_status;
 	int			txirq,rxirq,rtsirq;
-	int			have_rtscts:1;
+	unsigned int		have_rtscts:1;
 	struct clk		*clk;
 };
 
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/9 -v2] imx: serial: notify higher layers in case xmit IRQ was not called
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 1/9 -v2] imx: serial: fix whitespaces (no changes in functionality) Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 2/9 -v2] imx: serial: fix one bit field type Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 4/9 -v2] imx: serial: be sure to stop xmit upon shutdown Oskar Schirmer
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer, Fabian Godehardt

From: Fabian Godehardt <fg@emlix.com>

upper layers, namely line discipline, need to be notified
when transmission of more data is possible. For spurious
cases, where IRQ handling does not supply notification
for sure, it is given additionally here, when data has just
been transmitted and space in the buffer will most probably
be available.

Signed-off-by: Fabian Godehardt <fg@emlix.com>
Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/imx.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 5ee325f..afcec9e 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -308,6 +308,9 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
 			break;
 	}
 
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
 	if (uart_circ_empty(xmit))
 		imx_stop_tx(&sport->port);
 }
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/9 -v2] imx: serial: be sure to stop xmit upon shutdown
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
                   ` (2 preceding siblings ...)
  2009-06-10 12:08 ` [PATCH 3/9 -v2] imx: serial: notify higher layers in case xmit IRQ was not called Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 5/9 -v2] imx: serial: handle initialisation failure correctly Oskar Schirmer
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer, Fabian Godehardt

From: Fabian Godehardt <fg@emlix.com>

needed to avoid continued transmission by hardware
while software already shuts down, which might
cause dangling characters to show up in hardware
queues when restarting the device.

Signed-off-by: Fabian Godehardt <fg@emlix.com>
Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/imx.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index afcec9e..3fa07a9 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -634,6 +634,10 @@ static void imx_shutdown(struct uart_port *port)
 	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
 
+	temp = readl(sport->port.membase + UCR2);
+	temp &= ~(UCR2_TXEN);
+	writel(temp, sport->port.membase + UCR2);
+
 	/*
 	 * Stop our timer.
 	 */
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/9 -v2] imx: serial: handle initialisation failure correctly
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
                   ` (3 preceding siblings ...)
  2009-06-10 12:08 ` [PATCH 4/9 -v2] imx: serial: be sure to stop xmit upon shutdown Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 6/9] lib: isolate rational fractions helper function Oskar Schirmer
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer,
	Daniel Glöckner

From: Daniel Glöckner <dg@emlix.com>

correctly de-initialise device when setting up failed,
call to pdata->exit() was missing.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/imx.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 3fa07a9..60892a2 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -1155,10 +1155,15 @@ static int serial_imx_probe(struct platform_device *pdev)
 			goto clkput;
 	}
 
-	uart_add_one_port(&imx_reg, &sport->port);
+	ret = uart_add_one_port(&imx_reg, &sport->port);
+	if (ret)
+		goto deinit;
 	platform_set_drvdata(pdev, &sport->port);
 
 	return 0;
+deinit:
+	if (pdata->exit)
+		pdata->exit(pdev);
 clkput:
 	clk_put(sport->clk);
 	clk_disable(sport->clk);
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 6/9] lib: isolate rational fractions helper function
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
                   ` (4 preceding siblings ...)
  2009-06-10 12:08 ` [PATCH 5/9 -v2] imx: serial: handle initialisation failure correctly Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 7/9 -v2] imx: serial: use rational library function Oskar Schirmer
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer

Provide a helper function to determine optimum numerator
denominator value pairs taking into account restricted
register size. Useful especially with PLL and other clock
configurations.

Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 include/linux/rational.h |   19 ++++++++++++++
 lib/Kconfig              |    3 ++
 lib/Makefile             |    1 +
 lib/rational.c           |   62 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 85 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/rational.h
 create mode 100644 lib/rational.c

diff --git a/include/linux/rational.h b/include/linux/rational.h
new file mode 100644
index 0000000..4f532fc
--- /dev/null
+++ b/include/linux/rational.h
@@ -0,0 +1,19 @@
+/*
+ * rational fractions
+ *
+ * Copyright (C) 2009 emlix GmbH, Oskar Schirmer <os@emlix.com>
+ *
+ * helper functions when coping with rational numbers,
+ * e.g. when calculating optimum numerator/denominator pairs for
+ * pll configuration taking into account restricted register size
+ */
+
+#ifndef _LINUX_RATIONAL_H
+#define _LINUX_RATIONAL_H
+
+void rational_best_approximation(
+	unsigned long given_numerator, unsigned long given_denominator,
+	unsigned long max_numerator, unsigned long max_denominator,
+	unsigned long *best_numerator, unsigned long *best_denominator);
+
+#endif /* _LINUX_RATIONAL_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 8ade0a7..9960be0 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -10,6 +10,9 @@ menu "Library routines"
 config BITREVERSE
 	tristate
 
+config RATIONAL
+	boolean
+
 config GENERIC_FIND_FIRST_BIT
 	bool
 
diff --git a/lib/Makefile b/lib/Makefile
index 33a40e4..1f6edef 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -50,6 +50,7 @@ ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
 endif
 
 obj-$(CONFIG_BITREVERSE) += bitrev.o
+obj-$(CONFIG_RATIONAL)	+= rational.o
 obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC16)	+= crc16.o
 obj-$(CONFIG_CRC_T10DIF)+= crc-t10dif.o
diff --git a/lib/rational.c b/lib/rational.c
new file mode 100644
index 0000000..b3c099b
--- /dev/null
+++ b/lib/rational.c
@@ -0,0 +1,62 @@
+/*
+ * rational fractions
+ *
+ * Copyright (C) 2009 emlix GmbH, Oskar Schirmer <os@emlix.com>
+ *
+ * helper functions when coping with rational numbers
+ */
+
+#include <linux/rational.h>
+
+/*
+ * calculate best rational approximation for a given fraction
+ * taking into account restricted register size, e.g. to find
+ * appropriate values for a pll with 5 bit denominator and
+ * 8 bit numerator register fields, trying to set up with a
+ * frequency ratio of 3.1415, one would say:
+ *
+ * rational_best_approximation(31415, 10000,
+ *		(1 << 8) - 1, (1 << 5) - 1, &n, &d);
+ *
+ * you may look at given_numerator as a fixed point number,
+ * with the fractional part size described in given_denominator.
+ *
+ * for theoretical background, see:
+ * http://en.wikipedia.org/wiki/Continued_fraction
+ */
+
+void rational_best_approximation(
+	unsigned long given_numerator, unsigned long given_denominator,
+	unsigned long max_numerator, unsigned long max_denominator,
+	unsigned long *best_numerator, unsigned long *best_denominator)
+{
+	unsigned long n, d, n0, d0, n1, d1;
+	n = given_numerator;
+	d = given_denominator;
+	n0 = d1 = 0;
+	n1 = d0 = 1;
+	for (;;) {
+		unsigned long t, a;
+		if ((n1 > max_numerator) || (d1 > max_denominator)) {
+			n1 = n0;
+			d1 = d0;
+			break;
+		}
+		if (d == 0)
+			break;
+		t = d;
+		a = n / d;
+		d = n % d;
+		n = t;
+		t = n0 + a * n1;
+		n0 = n1;
+		n1 = t;
+		t = d0 + a * d1;
+		d0 = d1;
+		d1 = t;
+	}
+	*best_numerator = n1;
+	*best_denominator = d1;
+}
+
+EXPORT_SYMBOL(rational_best_approximation);
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 7/9 -v2] imx: serial: use rational library function
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
                   ` (5 preceding siblings ...)
  2009-06-10 12:08 ` [PATCH 6/9] lib: isolate rational fractions helper function Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-12  7:16   ` Sascha Hauer
  2009-06-10 12:08 ` [PATCH 8/9 -v2] imx: serial: add IrDA support to serial driver Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate Oskar Schirmer
  8 siblings, 1 reply; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer

for calculation of numerator and denominator
used in baud rate setting, use generic library function
for optimum settings.

Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/Kconfig |    1 +
 drivers/serial/imx.c   |   30 ++++++++++--------------------
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 343e3a3..0ecb4c4 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -833,6 +833,7 @@ config SERIAL_IMX
 	bool "IMX serial port support"
 	depends on ARM && (ARCH_IMX || ARCH_MXC)
 	select SERIAL_CORE
+	select RATIONAL
 	help
 	  If you have a machine based on a Motorola IMX CPU you
 	  can enable its onboard serial port by enabling this option.
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 60892a2..11f93e2 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -41,6 +41,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/clk.h>
+#include <linux/rational.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -670,7 +671,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	unsigned long flags;
 	unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
 	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
-	unsigned int div, num, denom, ufcr;
+	unsigned int div, ufcr;
+	unsigned long num, denom;
 
 	/*
 	 * If we don't support modem control lines, don't allow
@@ -772,32 +774,20 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	if (!div)
 		div = 1;
 
-	num = baud;
-	denom = port->uartclk / div / 16;
+	rational_best_approximation(16 * div * baud, sport->port.uartclk,
+		1 << 16, 1 << 16, &num, &denom);
 
-	/* shift num and denom right until they fit into 16 bits */
-	while (num > 0x10000 || denom > 0x10000) {
-		num >>= 1;
-		denom >>= 1;
-	}
-	if (num > 0)
-		num -= 1;
-	if (denom > 0)
-		denom -= 1;
-
-	writel(num, sport->port.membase + UBIR);
-	writel(denom, sport->port.membase + UBMR);
-
-	if (div == 7)
-		div = 6; /* 6 in RFDIV means divide by 7 */
-	else
-		div = 6 - div;
+	num -= 1;
+	denom -= 1;
 
 	ufcr = readl(sport->port.membase + UFCR);
 	ufcr = (ufcr & (~UFCR_RFDIV)) |
 	    (div << 7);
 	writel(ufcr, sport->port.membase + UFCR);
 
+	writel(num, sport->port.membase + UBIR);
+	writel(denom, sport->port.membase + UBMR);
+
 #ifdef ONEMS
 	writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
 #endif
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 8/9 -v2] imx: serial: add IrDA support to serial driver
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
                   ` (6 preceding siblings ...)
  2009-06-10 12:08 ` [PATCH 7/9 -v2] imx: serial: use rational library function Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-10 12:08 ` [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate Oskar Schirmer
  8 siblings, 0 replies; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer, Fabian Godehardt

From: Fabian Godehardt <fg@emlix.com>

Using the iMX serial driver with an IrDA device
needs extra peripheral settings and specific
timing depending on the transmitter circuitry used.

Signed-off-by: Fabian Godehardt <fg@emlix.com>
Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 arch/arm/plat-mxc/include/mach/imx-uart.h |    5 +
 drivers/serial/imx.c                      |  195 ++++++++++++++++++++++++++---
 2 files changed, 181 insertions(+), 19 deletions(-)

diff --git a/arch/arm/plat-mxc/include/mach/imx-uart.h b/arch/arm/plat-mxc/include/mach/imx-uart.h
index 599217b..f9bd17d 100644
--- a/arch/arm/plat-mxc/include/mach/imx-uart.h
+++ b/arch/arm/plat-mxc/include/mach/imx-uart.h
@@ -20,11 +20,16 @@
 #define ASMARM_ARCH_UART_H
 
 #define IMXUART_HAVE_RTSCTS (1<<0)
+#define IMXUART_IRDA        (1<<1)
 
 struct imxuart_platform_data {
 	int (*init)(struct platform_device *pdev);
 	int (*exit)(struct platform_device *pdev);
 	unsigned int flags;
+	void (*irda_enable)(int enable);
+	unsigned int irda_inv_rx:1;
+	unsigned int irda_inv_tx:1;
+	unsigned short transceiver_delay;
 };
 
 #endif
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 11f93e2..1c00e2a 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -8,6 +8,9 @@
  *  Author: Sascha Hauer <sascha@saschahauer.de>
  *  Copyright (C) 2004 Pengutronix
  *
+ *  Copyright (C) 2009 emlix GmbH
+ *  Author: Fabian Godehardt (added IrDA support for iMX)
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -41,6 +44,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/rational.h>
 
 #include <asm/io.h>
@@ -149,6 +153,7 @@
 #define  UCR4_DREN  	 (1<<0)  /* Recv data ready interrupt enable */
 #define  UFCR_RXTL_SHF   0       /* Receiver trigger level shift */
 #define  UFCR_RFDIV      (7<<7)  /* Reference freq divider mask */
+#define  UFCR_RFDIV_REG(x)	(((x) < 7 ? 6 - (x) : 6) << 7)
 #define  UFCR_TXTL_SHF   10      /* Transmitter trigger level shift */
 #define  USR1_PARITYERR  (1<<15) /* Parity error interrupt flag */
 #define  USR1_RTSS  	 (1<<14) /* RTS pin status */
@@ -213,9 +218,19 @@ struct imx_port {
 	unsigned int		old_status;
 	int			txirq,rxirq,rtsirq;
 	unsigned int		have_rtscts:1;
+	unsigned int		use_irda:1;
+	unsigned int		irda_inv_rx:1;
+	unsigned int		irda_inv_tx:1;
+	unsigned short		trcv_delay; /* transceiver delay */
 	struct clk		*clk;
 };
 
+#ifdef CONFIG_IRDA
+#define USE_IRDA(sport)	((sport)->use_irda)
+#else
+#define USE_IRDA(sport)	(0)
+#endif
+
 /*
  * Handle any change of modem status signal since we were last called.
  */
@@ -269,6 +284,48 @@ static void imx_stop_tx(struct uart_port *port)
 	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
 
+	if (USE_IRDA(sport)) {
+		/* half duplex - wait for end of transmission */
+		int n = 256;
+		while ((--n > 0) &&
+		      !(readl(sport->port.membase + USR2) & USR2_TXDC)) {
+			udelay(5);
+			barrier();
+		}
+		/*
+		 * irda transceiver - wait a bit more to avoid
+		 * cutoff, hardware dependent
+		 */
+		udelay(sport->trcv_delay);
+
+		/*
+		 * half duplex - reactivate receive mode,
+		 * flush receive pipe echo crap
+		 */
+		if (readl(sport->port.membase + USR2) & USR2_TXDC) {
+			temp = readl(sport->port.membase + UCR1);
+			temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN);
+			writel(temp, sport->port.membase + UCR1);
+
+			temp = readl(sport->port.membase + UCR4);
+			temp &= ~(UCR4_TCEN);
+			writel(temp, sport->port.membase + UCR4);
+
+			while (readl(sport->port.membase + URXD0) &
+			       URXD_CHARRDY)
+				barrier();
+
+			temp = readl(sport->port.membase + UCR1);
+			temp |= UCR1_RRDYEN;
+			writel(temp, sport->port.membase + UCR1);
+
+			temp = readl(sport->port.membase + UCR4);
+			temp |= UCR4_DREN;
+			writel(temp, sport->port.membase + UCR4);
+		}
+		return;
+	}
+
 	temp = readl(sport->port.membase + UCR1);
 	writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
 }
@@ -324,9 +381,30 @@ static void imx_start_tx(struct uart_port *port)
 	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
 
+	if (USE_IRDA(sport)) {
+		/* half duplex in IrDA mode; have to disable receive mode */
+		temp = readl(sport->port.membase + UCR4);
+		temp &= ~(UCR4_DREN);
+		writel(temp, sport->port.membase + UCR4);
+
+		temp = readl(sport->port.membase + UCR1);
+		temp &= ~(UCR1_RRDYEN);
+		writel(temp, sport->port.membase + UCR1);
+	}
+
 	temp = readl(sport->port.membase + UCR1);
 	writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
 
+	if (USE_IRDA(sport)) {
+		temp = readl(sport->port.membase + UCR1);
+		temp |= UCR1_TRDYEN;
+		writel(temp, sport->port.membase + UCR1);
+
+		temp = readl(sport->port.membase + UCR4);
+		temp |= UCR4_TCEN;
+		writel(temp, sport->port.membase + UCR4);
+	}
+
 	if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
 		imx_transmit_buffer(sport);
 }
@@ -536,12 +614,7 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 	if(!ufcr_rfdiv)
 		ufcr_rfdiv = 1;
 
-	if(ufcr_rfdiv >= 7)
-		ufcr_rfdiv = 6;
-	else
-		ufcr_rfdiv = 6 - ufcr_rfdiv;
-
-	val |= UFCR_RFDIV & (ufcr_rfdiv << 7);
+	val |= UFCR_RFDIV_REG(ufcr_rfdiv);
 
 	writel(val, sport->port.membase + UFCR);
 
@@ -560,8 +633,24 @@ static int imx_startup(struct uart_port *port)
 	 * requesting IRQs
 	 */
 	temp = readl(sport->port.membase + UCR4);
+
+	if (USE_IRDA(sport))
+		temp |= UCR4_IRSC;
+
 	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
 
+	if (USE_IRDA(sport)) {
+		/* reset fifo's and state machines */
+		int i = 100;
+		temp = readl(sport->port.membase + UCR2);
+		temp &= ~UCR2_SRST;
+		writel(temp, sport->port.membase + UCR2);
+		while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) &&
+		    (--i > 0)) {
+			udelay(1);
+		}
+	}
+
 	/*
 	 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
 	 * chips only have one interrupt.
@@ -577,12 +666,16 @@ static int imx_startup(struct uart_port *port)
 		if (retval)
 			goto error_out2;
 
-		retval = request_irq(sport->rtsirq, imx_rtsint,
-			     (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
-			       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-				DRIVER_NAME, sport);
-		if (retval)
-			goto error_out3;
+		/* do not use RTS IRQ on IrDA */
+		if (!USE_IRDA(sport)) {
+			retval = request_irq(sport->rtsirq, imx_rtsint,
+				     (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
+				       IRQF_TRIGGER_FALLING |
+				       IRQF_TRIGGER_RISING,
+					DRIVER_NAME, sport);
+			if (retval)
+				goto error_out3;
+		}
 	} else {
 		retval = request_irq(sport->port.irq, imx_int, 0,
 				DRIVER_NAME, sport);
@@ -599,18 +692,49 @@ static int imx_startup(struct uart_port *port)
 
 	temp = readl(sport->port.membase + UCR1);
 	temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
+
+	if (USE_IRDA(sport)) {
+		temp |= UCR1_IREN;
+		temp &= ~(UCR1_RTSDEN);
+	}
+
 	writel(temp, sport->port.membase + UCR1);
 
 	temp = readl(sport->port.membase + UCR2);
 	temp |= (UCR2_RXEN | UCR2_TXEN);
 	writel(temp, sport->port.membase + UCR2);
 
+	if (USE_IRDA(sport)) {
+		/* clear RX-FIFO */
+		int i = 64;
+		while ((--i > 0) &&
+			(readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
+			barrier();
+		}
+	}
+
 #if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
 	temp = readl(sport->port.membase + UCR3);
 	temp |= UCR3_RXDMUXSEL;
 	writel(temp, sport->port.membase + UCR3);
 #endif
 
+	if (USE_IRDA(sport)) {
+		temp = readl(sport->port.membase + UCR4);
+		if (sport->irda_inv_rx)
+			temp |= UCR4_INVR;
+		else
+			temp &= ~(UCR4_INVR);
+		writel(temp | UCR4_DREN, sport->port.membase + UCR4);
+
+		temp = readl(sport->port.membase + UCR3);
+		if (sport->irda_inv_tx)
+			temp |= UCR3_INVT;
+		else
+			temp &= ~(UCR3_INVT);
+		writel(temp, sport->port.membase + UCR3);
+	}
+
 	/*
 	 * Enable modem status interrupts
 	 */
@@ -618,6 +742,16 @@ static int imx_startup(struct uart_port *port)
 	imx_enable_ms(&sport->port);
 	spin_unlock_irqrestore(&sport->port.lock,flags);
 
+	if (USE_IRDA(sport)) {
+		struct imxuart_platform_data *pdata;
+		pdata = sport->port.dev->platform_data;
+		sport->irda_inv_rx = pdata->irda_inv_rx;
+		sport->irda_inv_tx = pdata->irda_inv_tx;
+		sport->trcv_delay = pdata->transceiver_delay;
+		if (pdata->irda_enable)
+			pdata->irda_enable(1);
+	}
+
 	return 0;
 
 error_out3:
@@ -639,6 +773,13 @@ static void imx_shutdown(struct uart_port *port)
 	temp &= ~(UCR2_TXEN);
 	writel(temp, sport->port.membase + UCR2);
 
+	if (USE_IRDA(sport)) {
+		struct imxuart_platform_data *pdata;
+		pdata = sport->port.dev->platform_data;
+		if (pdata->irda_enable)
+			pdata->irda_enable(0);
+	}
+
 	/*
 	 * Stop our timer.
 	 */
@@ -648,7 +789,8 @@ static void imx_shutdown(struct uart_port *port)
 	 * Free the interrupts
 	 */
 	if (sport->txirq > 0) {
-		free_irq(sport->rtsirq, sport);
+		if (!USE_IRDA(sport))
+			free_irq(sport->rtsirq, sport);
 		free_irq(sport->txirq, sport);
 		free_irq(sport->rxirq, sport);
 	} else
@@ -660,6 +802,9 @@ static void imx_shutdown(struct uart_port *port)
 
 	temp = readl(sport->port.membase + UCR1);
 	temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+	if (USE_IRDA(sport))
+		temp &= ~(UCR1_IREN);
+
 	writel(temp, sport->port.membase + UCR1);
 }
 
@@ -768,11 +913,19 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 			sport->port.membase + UCR2);
 	old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
 
-	div = sport->port.uartclk / (baud * 16);
-	if (div > 7)
-		div = 7;
-	if (!div)
+	if (USE_IRDA(sport)) {
+		/*
+		 * use maximum available submodule frequency to
+		 * avoid missing short pulses due to low sampling rate
+		 */
 		div = 1;
+	} else {
+		div = sport->port.uartclk / (baud * 16);
+		if (div > 7)
+			div = 7;
+		if (!div)
+			div = 1;
+	}
 
 	rational_best_approximation(16 * div * baud, sport->port.uartclk,
 		1 << 16, 1 << 16, &num, &denom);
@@ -781,8 +934,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	denom -= 1;
 
 	ufcr = readl(sport->port.membase + UFCR);
-	ufcr = (ufcr & (~UFCR_RFDIV)) |
-	    (div << 7);
+	ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
 	writel(ufcr, sport->port.membase + UFCR);
 
 	writel(num, sport->port.membase + UBIR);
@@ -1139,6 +1291,11 @@ static int serial_imx_probe(struct platform_device *pdev)
 	if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
 		sport->have_rtscts = 1;
 
+#ifdef CONFIG_IRDA
+	if (pdata && (pdata->flags & IMXUART_IRDA))
+		sport->use_irda = 1;
+#endif
+
 	if (pdata->init) {
 		ret = pdata->init(pdev);
 		if (ret)
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate
  2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
                   ` (7 preceding siblings ...)
  2009-06-10 12:08 ` [PATCH 8/9 -v2] imx: serial: add IrDA support to serial driver Oskar Schirmer
@ 2009-06-10 12:08 ` Oskar Schirmer
  2009-06-16  7:28   ` Sascha Hauer
  8 siblings, 1 reply; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-10 12:08 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-kernel, Andrew Morton, Alan Cox, Oskar Schirmer

real baud rate may be different from the one requested.
for upper layers, set the nearest value to the real rate
in favour of the rate previously requested.

Signed-off-by: Oskar Schirmer <os@emlix.com>
---
 drivers/serial/imx.c |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 1c00e2a..c367160 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -818,6 +818,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
 	unsigned int div, ufcr;
 	unsigned long num, denom;
+	uint64_t tdiv64;
 
 	/*
 	 * If we don't support modem control lines, don't allow
@@ -930,6 +931,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	rational_best_approximation(16 * div * baud, sport->port.uartclk,
 		1 << 16, 1 << 16, &num, &denom);
 
+	tdiv64 = sport->port.uartclk;
+	tdiv64 *= num;
+	do_div(tdiv64, denom * 16 * div);
+	tty_encode_baud_rate(sport->port.info->port.tty,
+		(speed_t)tdiv64, (speed_t)tdiv64);
+
 	num -= 1;
 	denom -= 1;
 
-- 
1.5.3.7


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 7/9 -v2] imx: serial: use rational library function
  2009-06-10 12:08 ` [PATCH 7/9 -v2] imx: serial: use rational library function Oskar Schirmer
@ 2009-06-12  7:16   ` Sascha Hauer
  2009-06-15  7:40     ` Oskar Schirmer
  0 siblings, 1 reply; 16+ messages in thread
From: Sascha Hauer @ 2009-06-12  7:16 UTC (permalink / raw)
  To: Oskar Schirmer; +Cc: Sascha Hauer, linux-kernel, Andrew Morton, Alan Cox

Hi Oskar,

On Wed, Jun 10, 2009 at 02:08:34PM +0200, Oskar Schirmer wrote:
> for calculation of numerator and denominator
> used in baud rate setting, use generic library function
> for optimum settings.
> 
> Signed-off-by: Oskar Schirmer <os@emlix.com>
> ---
>  drivers/serial/Kconfig |    1 +
>  drivers/serial/imx.c   |   30 ++++++++++--------------------
>  2 files changed, 11 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index 343e3a3..0ecb4c4 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -833,6 +833,7 @@ config SERIAL_IMX
>  	bool "IMX serial port support"
>  	depends on ARM && (ARCH_IMX || ARCH_MXC)
>  	select SERIAL_CORE
> +	select RATIONAL
>  	help
>  	  If you have a machine based on a Motorola IMX CPU you
>  	  can enable its onboard serial port by enabling this option.
> diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
> index 60892a2..11f93e2 100644
> --- a/drivers/serial/imx.c
> +++ b/drivers/serial/imx.c
> @@ -41,6 +41,7 @@
>  #include <linux/serial_core.h>
>  #include <linux/serial.h>
>  #include <linux/clk.h>
> +#include <linux/rational.h>
>  
>  #include <asm/io.h>
>  #include <asm/irq.h>
> @@ -670,7 +671,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>  	unsigned long flags;
>  	unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
>  	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
> -	unsigned int div, num, denom, ufcr;
> +	unsigned int div, ufcr;
> +	unsigned long num, denom;
>  
>  	/*
>  	 * If we don't support modem control lines, don't allow
> @@ -772,32 +774,20 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>  	if (!div)
>  		div = 1;
>  
> -	num = baud;
> -	denom = port->uartclk / div / 16;
> +	rational_best_approximation(16 * div * baud, sport->port.uartclk,
> +		1 << 16, 1 << 16, &num, &denom);

Reading the example you have given for rational_best_approximation() in
the last patch this should be (1 << 16) - 1, right?

>  
> -	/* shift num and denom right until they fit into 16 bits */
> -	while (num > 0x10000 || denom > 0x10000) {
> -		num >>= 1;
> -		denom >>= 1;
> -	}
> -	if (num > 0)
> -		num -= 1;
> -	if (denom > 0)
> -		denom -= 1;
> -
> -	writel(num, sport->port.membase + UBIR);
> -	writel(denom, sport->port.membase + UBMR);
> -
> -	if (div == 7)
> -		div = 6; /* 6 in RFDIV means divide by 7 */
> -	else
> -		div = 6 - div;
> +	num -= 1;
> +	denom -= 1;
>  
>  	ufcr = readl(sport->port.membase + UFCR);
>  	ufcr = (ufcr & (~UFCR_RFDIV)) |
>  	    (div << 7);
>  	writel(ufcr, sport->port.membase + UFCR);
>  
> +	writel(num, sport->port.membase + UBIR);
> +	writel(denom, sport->port.membase + UBMR);
> +
>  #ifdef ONEMS
>  	writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
>  #endif
> -- 
> 1.5.3.7
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 7/9 -v2] imx: serial: use rational library function
  2009-06-12  7:16   ` Sascha Hauer
@ 2009-06-15  7:40     ` Oskar Schirmer
  2009-06-15  8:15       ` Sascha Hauer
  0 siblings, 1 reply; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-15  7:40 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: Oskar Schirmer, Sascha Hauer, linux-kernel, Andrew Morton, Alan Cox

On Fri, Jun 12, 2009 at 09:16:49 +0200, Sascha Hauer wrote:
> Hi Oskar,
> 
> On Wed, Jun 10, 2009 at 02:08:34PM +0200, Oskar Schirmer wrote:
> > for calculation of numerator and denominator
> > used in baud rate setting, use generic library function
> > for optimum settings.
> > 
> > Signed-off-by: Oskar Schirmer <os@emlix.com>
> > ---
> >  drivers/serial/Kconfig |    1 +
> >  drivers/serial/imx.c   |   30 ++++++++++--------------------
> >  2 files changed, 11 insertions(+), 20 deletions(-)
> > 
> > diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> > index 343e3a3..0ecb4c4 100644
> > --- a/drivers/serial/Kconfig
> > +++ b/drivers/serial/Kconfig
> > @@ -833,6 +833,7 @@ config SERIAL_IMX
> >  	bool "IMX serial port support"
> >  	depends on ARM && (ARCH_IMX || ARCH_MXC)
> >  	select SERIAL_CORE
> > +	select RATIONAL
> >  	help
> >  	  If you have a machine based on a Motorola IMX CPU you
> >  	  can enable its onboard serial port by enabling this option.
> > diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
> > index 60892a2..11f93e2 100644
> > --- a/drivers/serial/imx.c
> > +++ b/drivers/serial/imx.c
> > @@ -41,6 +41,7 @@
> >  #include <linux/serial_core.h>
> >  #include <linux/serial.h>
> >  #include <linux/clk.h>
> > +#include <linux/rational.h>
> >  
> >  #include <asm/io.h>
> >  #include <asm/irq.h>
> > @@ -670,7 +671,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
> >  	unsigned long flags;
> >  	unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
> >  	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
> > -	unsigned int div, num, denom, ufcr;
> > +	unsigned int div, ufcr;
> > +	unsigned long num, denom;
> >  
> >  	/*
> >  	 * If we don't support modem control lines, don't allow
> > @@ -772,32 +774,20 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
> >  	if (!div)
> >  		div = 1;
> >  
> > -	num = baud;
> > -	denom = port->uartclk / div / 16;
> > +	rational_best_approximation(16 * div * baud, sport->port.uartclk,
> > +		1 << 16, 1 << 16, &num, &denom);
> 
> Reading the example you have given for rational_best_approximation() in
> the last patch this should be (1 << 16) - 1, right?

No, the registers hold values minus one, e.g.
to select numerator = 14, set UBIR = 13.
Thus, the maximum value for rate calculation
is 1 << 16, not 0xFFFF. For the "minus 1", see below.

> 
> >  
> > -	/* shift num and denom right until they fit into 16 bits */
> > -	while (num > 0x10000 || denom > 0x10000) {
> > -		num >>= 1;
> > -		denom >>= 1;
> > -	}
> > -	if (num > 0)
> > -		num -= 1;
> > -	if (denom > 0)
> > -		denom -= 1;
> > -
> > -	writel(num, sport->port.membase + UBIR);
> > -	writel(denom, sport->port.membase + UBMR);
> > -
> > -	if (div == 7)
> > -		div = 6; /* 6 in RFDIV means divide by 7 */
> > -	else
> > -		div = 6 - div;
> > +	num -= 1;
> > +	denom -= 1;

This is where rate calculation values are
decreased for register purpose.

> >  
> >  	ufcr = readl(sport->port.membase + UFCR);
> >  	ufcr = (ufcr & (~UFCR_RFDIV)) |
> >  	    (div << 7);
> >  	writel(ufcr, sport->port.membase + UFCR);
> >  
> > +	writel(num, sport->port.membase + UBIR);
> > +	writel(denom, sport->port.membase + UBMR);
> > +
> >  #ifdef ONEMS
> >  	writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
> >  #endif
> > -- 
> > 1.5.3.7
> > 
> > 
> 
> -- 
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 

-- 
oskar schirmer, emlix gmbh, http://www.emlix.com
fon +49 551 30664-0, fax -11, bahnhofsallee 1b, 37081 göttingen, germany
geschäftsführung: dr. uwe kracke, dr. cord seele, ust-idnr.: de 205 198 055
sitz der gesellschaft: göttingen, amtsgericht göttingen hr b 3160

emlix - your embedded linux partner

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 7/9 -v2] imx: serial: use rational library function
  2009-06-15  7:40     ` Oskar Schirmer
@ 2009-06-15  8:15       ` Sascha Hauer
  0 siblings, 0 replies; 16+ messages in thread
From: Sascha Hauer @ 2009-06-15  8:15 UTC (permalink / raw)
  To: Oskar Schirmer; +Cc: Sascha Hauer, linux-kernel, Andrew Morton, Alan Cox

On Mon, Jun 15, 2009 at 09:40:33AM +0200, Oskar Schirmer wrote:
> On Fri, Jun 12, 2009 at 09:16:49 +0200, Sascha Hauer wrote:
> > Hi Oskar,
> > 
> > On Wed, Jun 10, 2009 at 02:08:34PM +0200, Oskar Schirmer wrote:
> > > for calculation of numerator and denominator
> > > used in baud rate setting, use generic library function
> > > for optimum settings.
> > > 
> > > Signed-off-by: Oskar Schirmer <os@emlix.com>
> > > ---
> > >  drivers/serial/Kconfig |    1 +
> > >  drivers/serial/imx.c   |   30 ++++++++++--------------------
> > >  2 files changed, 11 insertions(+), 20 deletions(-)
> > > 
> > > diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> > > index 343e3a3..0ecb4c4 100644
> > > --- a/drivers/serial/Kconfig
> > > +++ b/drivers/serial/Kconfig
> > > @@ -833,6 +833,7 @@ config SERIAL_IMX
> > >  	bool "IMX serial port support"
> > >  	depends on ARM && (ARCH_IMX || ARCH_MXC)
> > >  	select SERIAL_CORE
> > > +	select RATIONAL
> > >  	help
> > >  	  If you have a machine based on a Motorola IMX CPU you
> > >  	  can enable its onboard serial port by enabling this option.
> > > diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
> > > index 60892a2..11f93e2 100644
> > > --- a/drivers/serial/imx.c
> > > +++ b/drivers/serial/imx.c
> > > @@ -41,6 +41,7 @@
> > >  #include <linux/serial_core.h>
> > >  #include <linux/serial.h>
> > >  #include <linux/clk.h>
> > > +#include <linux/rational.h>
> > >  
> > >  #include <asm/io.h>
> > >  #include <asm/irq.h>
> > > @@ -670,7 +671,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
> > >  	unsigned long flags;
> > >  	unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
> > >  	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
> > > -	unsigned int div, num, denom, ufcr;
> > > +	unsigned int div, ufcr;
> > > +	unsigned long num, denom;
> > >  
> > >  	/*
> > >  	 * If we don't support modem control lines, don't allow
> > > @@ -772,32 +774,20 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
> > >  	if (!div)
> > >  		div = 1;
> > >  
> > > -	num = baud;
> > > -	denom = port->uartclk / div / 16;
> > > +	rational_best_approximation(16 * div * baud, sport->port.uartclk,
> > > +		1 << 16, 1 << 16, &num, &denom);
> > 
> > Reading the example you have given for rational_best_approximation() in
> > the last patch this should be (1 << 16) - 1, right?
> 
> No, the registers hold values minus one, e.g.
> to select numerator = 14, set UBIR = 13.
> Thus, the maximum value for rate calculation
> is 1 << 16, not 0xFFFF. For the "minus 1", see below.

Ah, I see.
I'm fine with this patch and the rest of the series. so applied to
mxc-master.

Sascha

> 
> > 
> > >  
> > > -	/* shift num and denom right until they fit into 16 bits */
> > > -	while (num > 0x10000 || denom > 0x10000) {
> > > -		num >>= 1;
> > > -		denom >>= 1;
> > > -	}
> > > -	if (num > 0)
> > > -		num -= 1;
> > > -	if (denom > 0)
> > > -		denom -= 1;
> > > -
> > > -	writel(num, sport->port.membase + UBIR);
> > > -	writel(denom, sport->port.membase + UBMR);
> > > -
> > > -	if (div == 7)
> > > -		div = 6; /* 6 in RFDIV means divide by 7 */
> > > -	else
> > > -		div = 6 - div;
> > > +	num -= 1;
> > > +	denom -= 1;
> 
> This is where rate calculation values are
> decreased for register purpose.
> 
> > >  
> > >  	ufcr = readl(sport->port.membase + UFCR);
> > >  	ufcr = (ufcr & (~UFCR_RFDIV)) |
> > >  	    (div << 7);
> > >  	writel(ufcr, sport->port.membase + UFCR);
> > >  
> > > +	writel(num, sport->port.membase + UBIR);
> > > +	writel(denom, sport->port.membase + UBMR);
> > > +
> > >  #ifdef ONEMS
> > >  	writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
> > >  #endif
> > > -- 
> > > 1.5.3.7
> > > 
> > > 
> > 
> > -- 
> > Pengutronix e.K.                           |                             |
> > Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> > Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> > Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> > 
> 
> -- 
> oskar schirmer, emlix gmbh, http://www.emlix.com
> fon +49 551 30664-0, fax -11, bahnhofsallee 1b, 37081 göttingen, germany
> geschäftsführung: dr. uwe kracke, dr. cord seele, ust-idnr.: de 205 198 055
> sitz der gesellschaft: göttingen, amtsgericht göttingen hr b 3160
> 
> emlix - your embedded linux partner
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate
  2009-06-10 12:08 ` [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate Oskar Schirmer
@ 2009-06-16  7:28   ` Sascha Hauer
  0 siblings, 0 replies; 16+ messages in thread
From: Sascha Hauer @ 2009-06-16  7:28 UTC (permalink / raw)
  To: Oskar Schirmer; +Cc: Sascha Hauer, linux-kernel, Andrew Morton, Alan Cox

Hi,

On Wed, Jun 10, 2009 at 02:08:36PM +0200, Oskar Schirmer wrote:
> real baud rate may be different from the one requested.
> for upper layers, set the nearest value to the real rate
> in favour of the rate previously requested.
> 
> Signed-off-by: Oskar Schirmer <os@emlix.com>
> ---
>  drivers/serial/imx.c |    7 +++++++
>  1 files changed, 7 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
> index 1c00e2a..c367160 100644
> --- a/drivers/serial/imx.c
> +++ b/drivers/serial/imx.c
> @@ -818,6 +818,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>  	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
>  	unsigned int div, ufcr;
>  	unsigned long num, denom;
> +	uint64_t tdiv64;
>  
>  	/*
>  	 * If we don't support modem control lines, don't allow
> @@ -930,6 +931,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>  	rational_best_approximation(16 * div * baud, sport->port.uartclk,
>  		1 << 16, 1 << 16, &num, &denom);
>  
> +	tdiv64 = sport->port.uartclk;
> +	tdiv64 *= num;
> +	do_div(tdiv64, denom * 16 * div);
> +	tty_encode_baud_rate(sport->port.info->port.tty,
> +		(speed_t)tdiv64, (speed_t)tdiv64);
> +
>  	num -= 1;
>  	denom -= 1;
>  

This patch breaks booting with a serial console on i.MX UARTs. The
following patch fixes this. Alan, please consider applying it.

Sascha


>From 4ac2e4dbb3bd31d67a395740b1d4f8206441141b Mon Sep 17 00:00:00 2001
From: Sascha Hauer <s.hauer@pengutronix.de>
Date: Tue, 16 Jun 2009 09:24:57 +0200
Subject: [PATCH] i.MX serial driver: Check for NULL pointer deref before calling  tty_encode_baud_rate

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/serial/imx.c |   12 +++++++-----
 1 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 285b414..5d7b58f 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -924,11 +924,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	rational_best_approximation(16 * div * baud, sport->port.uartclk,
 		1 << 16, 1 << 16, &num, &denom);
 
-	tdiv64 = sport->port.uartclk;
-	tdiv64 *= num;
-	do_div(tdiv64, denom * 16 * div);
-	tty_encode_baud_rate(sport->port.info->port.tty,
-		(speed_t)tdiv64, (speed_t)tdiv64);
+	if (port->info && port->info->port.tty) {
+		tdiv64 = sport->port.uartclk;
+		tdiv64 *= num;
+		do_div(tdiv64, denom * 16 * div);
+		tty_encode_baud_rate(sport->port.info->port.tty,
+				(speed_t)tdiv64, (speed_t)tdiv64);
+	}
 
 	num -= 1;
 	denom -= 1;
-- 
1.6.3.1


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: imx: serial: add IrDA support to serial driver
  2009-06-09 13:46 imx: serial: add IrDA support to serial driver Oskar Schirmer
@ 2009-06-09 18:29 ` Sascha Hauer
  0 siblings, 0 replies; 16+ messages in thread
From: Sascha Hauer @ 2009-06-09 18:29 UTC (permalink / raw)
  To: Oskar Schirmer; +Cc: Sascha Hauer, linux-kernel

Hi Oskar,

On Tue, Jun 09, 2009 at 03:46:43PM +0200, Oskar Schirmer wrote:
> 
> Using the iMX serial driver with an IrDA device
> needs extra peripheral settings and specific
> timing depending on the transmitter circuitry used.
> 
> The first five patches fix minor problems in the
> generic part of the iMX serial driver
> 
> The sixth introduces a generic function to calculate optimum
> numerator denominator value pairs for PLL and clock
> register settings, the seventh patch uses this function
> in the iMX serial driver.
> 
> The last patch finally adds IrDA support to the iMX serial driver.
> 

Out of interest: On which i.MX type system(s) did you test the
IRDA support?

Sascha


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 16+ messages in thread

* imx: serial: add IrDA support to serial driver
@ 2009-06-09 13:46 Oskar Schirmer
  2009-06-09 18:29 ` Sascha Hauer
  0 siblings, 1 reply; 16+ messages in thread
From: Oskar Schirmer @ 2009-06-09 13:46 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-kernel


Using the iMX serial driver with an IrDA device
needs extra peripheral settings and specific
timing depending on the transmitter circuitry used.

The first five patches fix minor problems in the
generic part of the iMX serial driver

The sixth introduces a generic function to calculate optimum
numerator denominator value pairs for PLL and clock
register settings, the seventh patch uses this function
in the iMX serial driver.

The last patch finally adds IrDA support to the iMX serial driver.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2009-06-16  7:29 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-06-10 12:08 imx: serial: add IrDA support to serial driver Oskar Schirmer
2009-06-10 12:08 ` [PATCH 1/9 -v2] imx: serial: fix whitespaces (no changes in functionality) Oskar Schirmer
2009-06-10 12:08 ` [PATCH 2/9 -v2] imx: serial: fix one bit field type Oskar Schirmer
2009-06-10 12:08 ` [PATCH 3/9 -v2] imx: serial: notify higher layers in case xmit IRQ was not called Oskar Schirmer
2009-06-10 12:08 ` [PATCH 4/9 -v2] imx: serial: be sure to stop xmit upon shutdown Oskar Schirmer
2009-06-10 12:08 ` [PATCH 5/9 -v2] imx: serial: handle initialisation failure correctly Oskar Schirmer
2009-06-10 12:08 ` [PATCH 6/9] lib: isolate rational fractions helper function Oskar Schirmer
2009-06-10 12:08 ` [PATCH 7/9 -v2] imx: serial: use rational library function Oskar Schirmer
2009-06-12  7:16   ` Sascha Hauer
2009-06-15  7:40     ` Oskar Schirmer
2009-06-15  8:15       ` Sascha Hauer
2009-06-10 12:08 ` [PATCH 8/9 -v2] imx: serial: add IrDA support to serial driver Oskar Schirmer
2009-06-10 12:08 ` [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate Oskar Schirmer
2009-06-16  7:28   ` Sascha Hauer
  -- strict thread matches above, loose matches on Subject: below --
2009-06-09 13:46 imx: serial: add IrDA support to serial driver Oskar Schirmer
2009-06-09 18:29 ` Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).