2009-06-09 13:47:23

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 last patch finally adds IrDA support to the iMX serial driver.


2009-06-09 13:47:08

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 2/8] 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-09 13:47:35

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 3/8] 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-09 13:47:48

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 4/8] 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-09 13:47:59

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 5/8] 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-09 13:48:22

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 1/8] 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-09 13:48:35

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 6/8] 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 | 60 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 83 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..76f5a0c
--- /dev/null
+++ b/lib/rational.c
@@ -0,0 +1,60 @@
+/*
+ * 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;
+}
--
1.5.3.7

2009-06-09 13:48:48

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 7/8] 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-09 13:48:59

by Oskar Schirmer

[permalink] [raw]
Subject: [PATCH 8/8] 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/mach-imx/include/mach/imx-uart.h | 4 +
drivers/serial/imx.c | 207 ++++++++++++++++++++++++++---
2 files changed, 192 insertions(+), 19 deletions(-)

diff --git a/arch/arm/mach-imx/include/mach/imx-uart.h b/arch/arm/mach-imx/include/mach/imx-uart.h
index d54eb1d..01ea268 100644
--- a/arch/arm/mach-imx/include/mach/imx-uart.h
+++ b/arch/arm/mach-imx/include/mach/imx-uart.h
@@ -2,11 +2,15 @@
#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);
void (*exit)(struct platform_device *pdev);
unsigned int flags;
+#ifdef CONFIG_IRDA
+ void (*irda_enable)(int enable);
+#endif
};

#endif
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 11f93e2..305b6bf 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,21 @@ struct imx_port {
unsigned int old_status;
int txirq,rxirq,rtsirq;
unsigned int have_rtscts:1;
+#ifdef CONFIG_IRDA
+ unsigned int use_irda:1;
+ unsigned int irda_inv_rx:1;
+ unsigned int irda_inv_tx:1;
+ int trcv_delay; /* transceiver delay */
+#endif
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 +286,50 @@ static void imx_stop_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;

+#ifdef CONFIG_IRDA
+ if (sport->use_irda) {
+ /* 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;
+ }
+#endif
+
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
}
@@ -324,9 +385,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 +618,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 +637,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 +670,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 +696,51 @@ 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

+#ifdef CONFIG_IRDA
+ if (sport->use_irda) {
+ 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);
+ }
+#endif
+
/*
* Enable modem status interrupts
*/
@@ -618,6 +748,15 @@ static int imx_startup(struct uart_port *port)
imx_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock,flags);

+#ifdef CONFIG_IRDA
+ if (sport->use_irda) {
+ struct imxuart_platform_data *pdata;
+ pdata = sport->port.dev->platform_data;
+ if (pdata->irda_enable)
+ pdata->irda_enable(1);
+ }
+#endif
+
return 0;

error_out3:
@@ -639,6 +778,15 @@ static void imx_shutdown(struct uart_port *port)
temp &= ~(UCR2_TXEN);
writel(temp, sport->port.membase + UCR2);

+#ifdef CONFIG_IRDA
+ if (sport->use_irda) {
+ struct imxuart_platform_data *pdata;
+ pdata = sport->port.dev->platform_data;
+ if (pdata->irda_enable)
+ pdata->irda_enable(0);
+ }
+#endif
+
/*
* Stop our timer.
*/
@@ -648,7 +796,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 +809,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 +920,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 +941,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 +1298,16 @@ 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)) {
+ /* set defaults */
+ sport->irda_inv_rx = 0;
+ sport->irda_inv_tx = 0;
+ sport->trcv_delay = 50;
+ sport->use_irda = 1;
+ }
+#endif
+
if (pdata->init) {
ret = pdata->init(pdev);
if (ret)
--
1.5.3.7

2009-06-09 15:01:25

by Alan

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

> +#ifdef CONFIG_IRDA
> +#define USE_IRDA(sport) ((sport)->use_irda)
> +#else
> +#define USE_IRDA(sport) (0)
> +#endif
> +

You define this but only use it in about half the cases ?

> +#ifdef CONFIG_IRDA
> + if (sport->use_irda) {
> + /* half duplex - wait for end of transmission */
> + int n = 256;

Otherwise this series looks sound.

While you are fiddling with the baud rate the driver really should work
out the resulting actual baud rate and call

tty_encode_baud_rate(tty, in-rate, out-rate);

that will report back the resulting rate and knows how to encode all the
termios fields as well as reporting close values to the requested speed
as the speed requested (eg if you ask for B38400 and it tries to encode
38215 or similar it will encode back B38400)

Alan

2009-06-09 16:18:30

by Oskar Schirmer

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

On Tue, Jun 09, 2009 at 16:01:32 +0100, Alan Cox wrote:
> > +#ifdef CONFIG_IRDA
> > +#define USE_IRDA(sport) ((sport)->use_irda)
> > +#else
> > +#define USE_IRDA(sport) (0)
> > +#endif
> > +
>
> You define this but only use it in about half the cases ?

Yes, with CONFIG_IRDA undefined several locations look like e.g.:

if (0) {
... (sport)->irda_inv_rx ...
}

Sure, the path will be optimized away, but still
the conditional statement is subjected to syntax
and scope analysis and gcc will correctly complain
that irda_inv_rx is not declared.

On the other hand, we could omit the USE_IRDA macro
altogether, but then we would have even more ifdefs
around.

>
> > +#ifdef CONFIG_IRDA
> > + if (sport->use_irda) {
> > + /* half duplex - wait for end of transmission */
> > + int n = 256;
>
> Otherwise this series looks sound.
>
> While you are fiddling with the baud rate the driver really should work
> out the resulting actual baud rate and call
>
> tty_encode_baud_rate(tty, in-rate, out-rate);
>
> that will report back the resulting rate and knows how to encode all the
> termios fields as well as reporting close values to the requested speed
> as the speed requested (eg if you ask for B38400 and it tries to encode
> 38215 or similar it will encode back B38400)

Ok, the driver will need another fix here with respect to its
interface to the upper layers. But the changes in
baud rate calculation we made are with focus on correct
peripheral register settings, because the old baud rate
calculation was not accurate enough for IrDA use.
That's why we introduced the approximation function
(and finally generalised it into lib/).

Oskar

2009-06-09 17:07:39

by Alan

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

> Sure, the path will be optimized away, but still
> the conditional statement is subjected to syntax
> and scope analysis and gcc will correctly complain
> that irda_inv_rx is not declared.

Why are only about half of them done this way rather than all ?

> peripheral register settings, because the old baud rate
> calculation was not accurate enough for IrDA use.
> That's why we introduced the approximation function
> (and finally generalised it into lib/).

Yes - I'm fine with that part.

Alan

2009-06-09 18:04:28

by Oskar Schirmer

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

On Tue, Jun 09, 2009 at 18:07:38 +0100, Alan Cox wrote:
> > Sure, the path will be optimized away, but still
> > the conditional statement is subjected to syntax
> > and scope analysis and gcc will correctly complain
> > that irda_inv_rx is not declared.
>
> Why are only about half of them done this way rather than all ?

Because only half of them use IrDA specific struct fields
directly, thus need to be ifdef'd explicitely.

Like I wrote before, we could replace all the

if (USE_IRDA(x)) ...

with some

#ifdef CONFIG_IRDA
if (x->use_irda) ...

but that way we would have more ifdef's around.
A matter of taste, I'ld say. If you vote for the
latter, we can adjust the patch accordingly, of
course.

Oskar

2009-06-09 18:30:51

by Sascha Hauer

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

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 |

2009-06-09 18:31:35

by Sascha Hauer

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

On Tue, Jun 09, 2009 at 03:46:49PM +0200, Oskar Schirmer wrote:
> 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 | 60 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 83 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..76f5a0c
> --- /dev/null
> +++ b/lib/rational.c
> @@ -0,0 +1,60 @@
> +/*
> + * 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(_GPL)?

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 |

2009-06-09 18:31:55

by Sascha Hauer

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

Hi Oskar,

Some comments inline.

On Tue, Jun 09, 2009 at 03:46:51PM +0200, Oskar Schirmer wrote:
> 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/mach-imx/include/mach/imx-uart.h | 4 +
> drivers/serial/imx.c | 207 ++++++++++++++++++++++++++---
> 2 files changed, 192 insertions(+), 19 deletions(-)
>
> diff --git a/arch/arm/mach-imx/include/mach/imx-uart.h b/arch/arm/mach-imx/include/mach/imx-uart.h
> index d54eb1d..01ea268 100644
> --- a/arch/arm/mach-imx/include/mach/imx-uart.h
> +++ b/arch/arm/mach-imx/include/mach/imx-uart.h
> @@ -2,11 +2,15 @@
> #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);
> void (*exit)(struct platform_device *pdev);
> unsigned int flags;
> +#ifdef CONFIG_IRDA
> + void (*irda_enable)(int enable);
> +#endif

I think you shouldn't ifdef this field. Otherwise platform code needs
exactly this ifdef aswell.

> };
>
> #endif
> diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
> index 11f93e2..305b6bf 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,21 @@ struct imx_port {
> unsigned int old_status;
> int txirq,rxirq,rtsirq;
> unsigned int have_rtscts:1;
> +#ifdef CONFIG_IRDA
> + unsigned int use_irda:1;
> + unsigned int irda_inv_rx:1;
> + unsigned int irda_inv_tx:1;
> + int trcv_delay; /* transceiver delay */
> +#endif

You can't use USE_IRDA() several times in your patch because you ifdef
these fields. Given the fact that these only add 4 bytes to struct
imx_port you could remove the ifdef here and use USE_IRDA()
consistently.

Do you need these fields anyway? Their values are hardcoded in the
driver and can't be overridden by platform code.

Sascha

> 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 +286,50 @@ static void imx_stop_tx(struct uart_port *port)
> struct imx_port *sport = (struct imx_port *)port;
> unsigned long temp;
>
> +#ifdef CONFIG_IRDA
> + if (sport->use_irda) {
> + /* 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;
> + }
> +#endif
> +
> temp = readl(sport->port.membase + UCR1);
> writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
> }
> @@ -324,9 +385,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 +618,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 +637,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 +670,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 +696,51 @@ 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
>
> +#ifdef CONFIG_IRDA
> + if (sport->use_irda) {
> + 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);
> + }
> +#endif
> +
> /*
> * Enable modem status interrupts
> */
> @@ -618,6 +748,15 @@ static int imx_startup(struct uart_port *port)
> imx_enable_ms(&sport->port);
> spin_unlock_irqrestore(&sport->port.lock,flags);
>
> +#ifdef CONFIG_IRDA
> + if (sport->use_irda) {
> + struct imxuart_platform_data *pdata;
> + pdata = sport->port.dev->platform_data;
> + if (pdata->irda_enable)
> + pdata->irda_enable(1);
> + }
> +#endif
> +
> return 0;
>
> error_out3:
> @@ -639,6 +778,15 @@ static void imx_shutdown(struct uart_port *port)
> temp &= ~(UCR2_TXEN);
> writel(temp, sport->port.membase + UCR2);
>
> +#ifdef CONFIG_IRDA
> + if (sport->use_irda) {
> + struct imxuart_platform_data *pdata;
> + pdata = sport->port.dev->platform_data;
> + if (pdata->irda_enable)
> + pdata->irda_enable(0);
> + }
> +#endif
> +
> /*
> * Stop our timer.
> */
> @@ -648,7 +796,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 +809,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 +920,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 +941,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 +1298,16 @@ 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)) {
> + /* set defaults */
> + sport->irda_inv_rx = 0;
> + sport->irda_inv_tx = 0;
> + sport->trcv_delay = 50;
> + sport->use_irda = 1;
> + }
> +#endif
> +
> if (pdata->init) {
> ret = pdata->init(pdev);
> if (ret)
> --
> 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-10 07:01:13

by Oskar Schirmer

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

On Tue, Jun 09, 2009 at 20:10:38 +0200, Sascha Hauer wrote:
> Hi Oskar,
>
> Some comments inline.
>
> On Tue, Jun 09, 2009 at 03:46:51PM +0200, Oskar Schirmer wrote:
> > 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/mach-imx/include/mach/imx-uart.h | 4 +
> > drivers/serial/imx.c | 207 ++++++++++++++++++++++++++---
> > 2 files changed, 192 insertions(+), 19 deletions(-)
> >
> > diff --git a/arch/arm/mach-imx/include/mach/imx-uart.h b/arch/arm/mach-imx/include/mach/imx-uart.h
> > index d54eb1d..01ea268 100644
> > --- a/arch/arm/mach-imx/include/mach/imx-uart.h
> > +++ b/arch/arm/mach-imx/include/mach/imx-uart.h
> > @@ -2,11 +2,15 @@
> > #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);
> > void (*exit)(struct platform_device *pdev);
> > unsigned int flags;
> > +#ifdef CONFIG_IRDA
> > + void (*irda_enable)(int enable);
> > +#endif
>
> I think you shouldn't ifdef this field. Otherwise platform code needs
> exactly this ifdef aswell.

Right. I'll change that.

> > };
> >
> > #endif
> > diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
> > index 11f93e2..305b6bf 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,21 @@ struct imx_port {
> > unsigned int old_status;
> > int txirq,rxirq,rtsirq;
> > unsigned int have_rtscts:1;
> > +#ifdef CONFIG_IRDA
> > + unsigned int use_irda:1;
> > + unsigned int irda_inv_rx:1;
> > + unsigned int irda_inv_tx:1;
> > + int trcv_delay; /* transceiver delay */
> > +#endif
>
> You can't use USE_IRDA() several times in your patch because you ifdef
> these fields. Given the fact that these only add 4 bytes to struct
> imx_port you could remove the ifdef here and use USE_IRDA()
> consistently.

Ok.

> Do you need these fields anyway? Their values are hardcoded in the
> driver and can't be overridden by platform code.

We used to set them via sysfs, but we thought the patch for that
is not generically reusable. I'll see if I add the default settings
to platform data, so they wont be hardcoded but can be adjusted
to the hardware used as needed.

Oskar

>
> Sascha
>
> > 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 +286,50 @@ static void imx_stop_tx(struct uart_port *port)
> > struct imx_port *sport = (struct imx_port *)port;
> > unsigned long temp;
> >
> > +#ifdef CONFIG_IRDA
> > + if (sport->use_irda) {
> > + /* 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;
> > + }
> > +#endif
> > +
> > temp = readl(sport->port.membase + UCR1);
> > writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
> > }
> > @@ -324,9 +385,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 +618,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 +637,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 +670,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 +696,51 @@ 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
> >
> > +#ifdef CONFIG_IRDA
> > + if (sport->use_irda) {
> > + 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);
> > + }
> > +#endif
> > +
> > /*
> > * Enable modem status interrupts
> > */
> > @@ -618,6 +748,15 @@ static int imx_startup(struct uart_port *port)
> > imx_enable_ms(&sport->port);
> > spin_unlock_irqrestore(&sport->port.lock,flags);
> >
> > +#ifdef CONFIG_IRDA
> > + if (sport->use_irda) {
> > + struct imxuart_platform_data *pdata;
> > + pdata = sport->port.dev->platform_data;
> > + if (pdata->irda_enable)
> > + pdata->irda_enable(1);
> > + }
> > +#endif
> > +
> > return 0;
> >
> > error_out3:
> > @@ -639,6 +778,15 @@ static void imx_shutdown(struct uart_port *port)
> > temp &= ~(UCR2_TXEN);
> > writel(temp, sport->port.membase + UCR2);
> >
> > +#ifdef CONFIG_IRDA
> > + if (sport->use_irda) {
> > + struct imxuart_platform_data *pdata;
> > + pdata = sport->port.dev->platform_data;
> > + if (pdata->irda_enable)
> > + pdata->irda_enable(0);
> > + }
> > +#endif
> > +
> > /*
> > * Stop our timer.
> > */
> > @@ -648,7 +796,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 +809,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 +920,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 +941,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 +1298,16 @@ 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)) {
> > + /* set defaults */
> > + sport->irda_inv_rx = 0;
> > + sport->irda_inv_tx = 0;
> > + sport->trcv_delay = 50;
> > + sport->use_irda = 1;
> > + }
> > +#endif
> > +
> > if (pdata->init) {
> > ret = pdata->init(pdev);
> > if (ret)
> > --
> > 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