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