2009-06-10 12:09:31

by Oskar Schirmer

[permalink] [raw]
Subject: imx: serial: add IrDA support to serial driver


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.


2009-06-10 12:08:48

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 4/9 -v2] imx: serial: be sure to stop xmit upon shutdown

From: Fabian Godehardt <[email protected]>

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 <[email protected]>
Signed-off-by: Oskar Schirmer <[email protected]>
---
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

2009-06-10 12:09:00

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 3/9 -v2] imx: serial: notify higher layers in case xmit IRQ was not called

From: Fabian Godehardt <[email protected]>

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 <[email protected]>
Signed-off-by: Oskar Schirmer <[email protected]>
---
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

2009-06-10 12:09:20

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate

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 <[email protected]>
---
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

2009-06-10 12:09:43

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 5/9 -v2] imx: serial: handle initialisation failure correctly

From: Daniel Glöckner <[email protected]>

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

Signed-off-by: Daniel Glöckner <[email protected]>
Signed-off-by: Oskar Schirmer <[email protected]>
---
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

2009-06-10 12:09:54

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 2/9 -v2] imx: serial: fix one bit field type

From: Daniel Glöckner <[email protected]>

"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 <[email protected]>
Signed-off-by: Oskar Schirmer <[email protected]>
---
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

2009-06-10 12:10:09

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 6/9] lib: isolate rational fractions helper function

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 <[email protected]>
---
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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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

2009-06-10 12:10:31

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 7/9 -v2] imx: serial: use rational library function

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

Signed-off-by: Oskar Schirmer <[email protected]>
---
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

2009-06-10 12:10:45

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 1/9 -v2] imx: serial: fix whitespaces (no changes in functionality)

Signed-off-by: Oskar Schirmer <[email protected]>
---
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

2009-06-10 12:10:58

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 8/9 -v2] imx: serial: add IrDA support to serial driver

From: Fabian Godehardt <[email protected]>

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 <[email protected]>
Signed-off-by: Oskar Schirmer <[email protected]>
---
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 <[email protected]>
* 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

2009-06-12 07:17:56

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 7/9 -v2] imx: serial: use rational library function

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 <[email protected]>
> ---
> 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 |

2009-06-15 07:40:45

by Oskar Schirmer

[permalink] [raw]
Subject: Re: [PATCH 7/9 -v2] imx: serial: use rational library function

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 <[email protected]>
> > ---
> > 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

2009-06-15 08:16:24

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 7/9 -v2] imx: serial: use rational library function

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 <[email protected]>
> > > ---
> > > 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 |

2009-06-16 07:29:12

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 9/9 -v2] imx: serial: use tty_encode_baud_rate to set true rate

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 <[email protected]>
> ---
> 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 <[email protected]>
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 <[email protected]>
---
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 |