* 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
#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>
#define UBIR 0xa4 /* BRM Incremental Register */
#define UBMR 0xa8 /* BRM Modulator Register */
#define UBRC 0xac /* Baud Rate Count Register */
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
-#define ONEMS 0xb0 /* One Millisecond register */
-#define UTS 0xb4 /* UART Test Register */
-#endif
-#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
-#define BIPR1 0xb0 /* Incremental Preset Register 1 */
-#define BIPR2 0xb4 /* Incremental Preset Register 2 */
-#define BIPR3 0xb8 /* Incremental Preset Register 3 */
-#define BIPR4 0xbc /* Incremental Preset Register 4 */
-#define BMPR1 0xc0 /* BRM Modulator Register 1 */
-#define BMPR2 0xc4 /* BRM Modulator Register 2 */
-#define BMPR3 0xc8 /* BRM Modulator Register 3 */
-#define BMPR4 0xcc /* BRM Modulator Register 4 */
-#define UTS 0xd0 /* UART Test Register */
-#endif
+#define MX2_ONEMS 0xb0 /* One Millisecond register */
+#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */
/* UART Control Register Bit Fields.*/
#define URXD_CHARRDY (1<<15)
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
-#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
-#endif
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
-#define UCR1_UARTCLKEN (0) /* not present on mx2/mx3 */
-#endif
+#define MX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, mx1 only */
#define UCR1_DOZE (1<<1) /* Doze */
#define UCR1_UARTEN (1<<0) /* UART enabled */
#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
-#ifdef CONFIG_ARCH_IMX
-#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
-#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
-#endif
-#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
-#define UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */
-#endif
+#define MX1_UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
+#define MX1_UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
+#define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */
#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
#define UCR3_BPEN (1<<0) /* Preset registers enable */
#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */
#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 */
#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
-#ifdef CONFIG_ARCH_IMX
-#define SERIAL_IMX_MAJOR 204
-#define MINOR_START 41
-#define DEV_NAME "ttySMX"
-#define MAX_INTERNAL_IRQ IMX_IRQS
-#endif
-
-#ifdef CONFIG_ARCH_MXC
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS
-#endif
/*
* This determines how often we check the modem status signals
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.
*/
if (changed & TIOCM_CTS)
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
- wake_up_interruptible(&sport->port.info->delta_msr_wait);
+ wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
}
/*
struct imx_port *sport = (struct imx_port *)data;
unsigned long flags;
- if (sport->port.info) {
+ if (sport->port.state) {
spin_lock_irqsave(&sport->port.lock, flags);
imx_mctrl_check(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
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);
}
static inline void imx_transmit_buffer(struct imx_port *sport)
{
- struct circ_buf *xmit = &sport->port.info->xmit;
+ struct circ_buf *xmit = &sport->port.state->xmit;
while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
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);
}
writel(USR1_RTSD, sport->port.membase + USR1);
uart_handle_cts_change(&sport->port, !!val);
- wake_up_interruptible(&sport->port.info->delta_msr_wait);
+ wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
static irqreturn_t imx_txint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- struct circ_buf *xmit = &sport->port.info->xmit;
+ struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock,flags);
{
struct imx_port *sport = dev_id;
unsigned int rx,flg,ignored = 0;
- struct tty_struct *tty = sport->port.info->port.tty;
+ struct tty_struct *tty = sport->port.state->port.tty;
unsigned long flags, temp;
spin_lock_irqsave(&sport->port.lock,flags);
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);
* 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.
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);
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 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)) {
+ /* clear RX-FIFO */
+ int i = 64;
+ while ((--i > 0) &&
+ (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
+ barrier();
+ }
+ }
+
+ if (!cpu_is_mx1()) {
+ temp = readl(sport->port.membase + UCR3);
+ temp |= MX2_UCR3_RXDMUXSEL;
+ writel(temp, sport->port.membase + UCR3);
+ }
+
+ 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
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:
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.
*/
* 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
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);
}
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
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);
+ if (port->state && port->state->port.tty) {
+ tdiv64 = sport->port.uartclk;
+ tdiv64 *= num;
+ do_div(tdiv64, denom * 16 * div);
+ tty_encode_baud_rate(sport->port.state->port.tty,
+ (speed_t)tdiv64, (speed_t)tdiv64);
+ }
+
num -= 1;
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);
writel(denom, sport->port.membase + UBMR);
-#ifdef ONEMS
- writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
-#endif
+ if (!cpu_is_mx1())
+ writel(sport->port.uartclk / div / 1000,
+ sport->port.membase + MX2_ONEMS);
writel(old_ucr1, sport->port.membase + UCR1);
imx_console_write(struct console *co, const char *s, unsigned int count)
{
struct imx_port *sport = imx_ports[co->index];
- unsigned int old_ucr1, old_ucr2;
+ unsigned int old_ucr1, old_ucr2, ucr1;
/*
* First, save UCR1/2 and then disable interrupts
*/
- old_ucr1 = readl(sport->port.membase + UCR1);
+ ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
old_ucr2 = readl(sport->port.membase + UCR2);
- writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) &
- ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
- sport->port.membase + UCR1);
+ if (cpu_is_mx1())
+ ucr1 |= MX1_UCR1_UARTCLKEN;
+ ucr1 |= UCR1_UARTEN;
+ ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+
+ writel(ucr1, sport->port.membase + UCR1);
writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
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)