blob: 0de81f71f8848f95d11f72b0b43f6d23768861e5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/serial/imx.c
3 *
4 * Driver for Motorola IMX serial ports
5 *
6 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
7 *
8 * Author: Sascha Hauer <sascha@saschahauer.de>
9 * Copyright (C) 2004 Pengutronix
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * [29-Mar-2005] Mike Lee
26 * Added hardware handshake
27 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
30#define SUPPORT_SYSRQ
31#endif
32
33#include <linux/module.h>
34#include <linux/ioport.h>
35#include <linux/init.h>
36#include <linux/console.h>
37#include <linux/sysrq.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010038#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/tty.h>
40#include <linux/tty_flip.h>
41#include <linux/serial_core.h>
42#include <linux/serial.h>
Sascha Hauer38a41fd2008-07-05 10:02:46 +020043#include <linux/clk.h>
Oskar Schirmer534fca02009-06-11 14:52:23 +010044#include <linux/rational.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46#include <asm/io.h>
47#include <asm/irq.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010048#include <mach/hardware.h>
49#include <mach/imx-uart.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Sascha Hauerff4bfb22007-04-26 08:26:13 +010051/* Register definitions */
52#define URXD0 0x0 /* Receiver Register */
53#define URTX0 0x40 /* Transmitter Register */
54#define UCR1 0x80 /* Control Register 1 */
55#define UCR2 0x84 /* Control Register 2 */
56#define UCR3 0x88 /* Control Register 3 */
57#define UCR4 0x8c /* Control Register 4 */
58#define UFCR 0x90 /* FIFO Control Register */
59#define USR1 0x94 /* Status Register 1 */
60#define USR2 0x98 /* Status Register 2 */
61#define UESC 0x9c /* Escape Character Register */
62#define UTIM 0xa0 /* Escape Timer Register */
63#define UBIR 0xa4 /* BRM Incremental Register */
64#define UBMR 0xa8 /* BRM Modulator Register */
65#define UBRC 0xac /* Baud Rate Count Register */
Sascha Hauer604cbad2008-07-05 10:02:58 +020066#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
Sascha Hauere3d13ff2008-07-05 10:02:48 +020067#define ONEMS 0xb0 /* One Millisecond register */
68#define UTS 0xb4 /* UART Test Register */
69#endif
Paulius Zaleckasbd006a92008-11-14 11:01:39 +010070#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
Sascha Hauerff4bfb22007-04-26 08:26:13 +010071#define BIPR1 0xb0 /* Incremental Preset Register 1 */
72#define BIPR2 0xb4 /* Incremental Preset Register 2 */
73#define BIPR3 0xb8 /* Incremental Preset Register 3 */
74#define BIPR4 0xbc /* Incremental Preset Register 4 */
75#define BMPR1 0xc0 /* BRM Modulator Register 1 */
76#define BMPR2 0xc4 /* BRM Modulator Register 2 */
77#define BMPR3 0xc8 /* BRM Modulator Register 3 */
78#define BMPR4 0xcc /* BRM Modulator Register 4 */
79#define UTS 0xd0 /* UART Test Register */
Sascha Hauere3d13ff2008-07-05 10:02:48 +020080#endif
Sascha Hauerff4bfb22007-04-26 08:26:13 +010081
82/* UART Control Register Bit Fields.*/
83#define URXD_CHARRDY (1<<15)
84#define URXD_ERR (1<<14)
85#define URXD_OVRRUN (1<<13)
86#define URXD_FRMERR (1<<12)
87#define URXD_BRK (1<<11)
88#define URXD_PRERR (1<<10)
89#define UCR1_ADEN (1<<15) /* Auto dectect interrupt */
90#define UCR1_ADBR (1<<14) /* Auto detect baud rate */
91#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
92#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
93#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
94#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
95#define UCR1_IREN (1<<7) /* Infrared interface enable */
96#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
97#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
98#define UCR1_SNDBRK (1<<4) /* Send break */
99#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
Paulius Zaleckasbd006a92008-11-14 11:01:39 +0100100#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100101#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200102#endif
Sascha Hauer604cbad2008-07-05 10:02:58 +0200103#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200104#define UCR1_UARTCLKEN (0) /* not present on mx2/mx3 */
105#endif
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100106#define UCR1_DOZE (1<<1) /* Doze */
107#define UCR1_UARTEN (1<<0) /* UART enabled */
108#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
109#define UCR2_IRTS (1<<14) /* Ignore RTS pin */
110#define UCR2_CTSC (1<<13) /* CTS pin control */
111#define UCR2_CTS (1<<12) /* Clear to send */
112#define UCR2_ESCEN (1<<11) /* Escape enable */
113#define UCR2_PREN (1<<8) /* Parity enable */
114#define UCR2_PROE (1<<7) /* Parity odd/even */
115#define UCR2_STPB (1<<6) /* Stop */
116#define UCR2_WS (1<<5) /* Word size */
117#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */
118#define UCR2_TXEN (1<<2) /* Transmitter enabled */
119#define UCR2_RXEN (1<<1) /* Receiver enabled */
120#define UCR2_SRST (1<<0) /* SW reset */
121#define UCR3_DTREN (1<<13) /* DTR interrupt enable */
122#define UCR3_PARERREN (1<<12) /* Parity enable */
123#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */
124#define UCR3_DSR (1<<10) /* Data set ready */
125#define UCR3_DCD (1<<9) /* Data carrier detect */
126#define UCR3_RI (1<<8) /* Ring indicator */
127#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */
128#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
129#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
130#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
Marc Kleine-Budde44118052008-07-28 12:10:34 +0200131#ifdef CONFIG_ARCH_IMX
132#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
133#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
134#endif
135#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
136#define UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */
137#endif
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100138#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
139#define UCR3_BPEN (1<<0) /* Preset registers enable */
140#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */
141#define UCR4_INVR (1<<9) /* Inverted infrared reception */
142#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
143#define UCR4_WKEN (1<<7) /* Wake interrupt enable */
144#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
145#define UCR4_IRSC (1<<5) /* IR special case */
146#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
147#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
148#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */
149#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
150#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
151#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
152#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
153#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
154#define USR1_RTSS (1<<14) /* RTS pin status */
155#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
156#define USR1_RTSD (1<<12) /* RTS delta */
157#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */
158#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */
159#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */
160#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */
161#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */
162#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */
163#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */
164#define USR2_ADET (1<<15) /* Auto baud rate detect complete */
165#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */
166#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */
167#define USR2_IDLE (1<<12) /* Idle condition */
168#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */
169#define USR2_WAKE (1<<7) /* Wake */
170#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */
171#define USR2_TXDC (1<<3) /* Transmitter complete */
172#define USR2_BRCD (1<<2) /* Break condition */
173#define USR2_ORE (1<<1) /* Overrun error */
174#define USR2_RDR (1<<0) /* Recv data ready */
175#define UTS_FRCPERR (1<<13) /* Force parity error */
176#define UTS_LOOP (1<<12) /* Loop tx and rx */
177#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */
178#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */
179#define UTS_TXFULL (1<<4) /* TxFIFO full */
180#define UTS_RXFULL (1<<3) /* RxFIFO full */
181#define UTS_SOFTRST (1<<0) /* Software reset */
182
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183/* We've been assigned a range on the "Low-density serial ports" major */
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200184#ifdef CONFIG_ARCH_IMX
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#define SERIAL_IMX_MAJOR 204
186#define MINOR_START 41
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200187#define DEV_NAME "ttySMX"
188#define MAX_INTERNAL_IRQ IMX_IRQS
189#endif
190
Paulius Zaleckasbd006a92008-11-14 11:01:39 +0100191#ifdef CONFIG_ARCH_MXC
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200192#define SERIAL_IMX_MAJOR 207
193#define MINOR_START 16
194#define DEV_NAME "ttymxc"
Sascha Hauer9d631b82008-12-18 11:08:55 +0100195#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200196#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 * This determines how often we check the modem status signals
200 * for any change. They generally aren't connected to an IRQ
201 * so we have to poll them. We also check immediately before
202 * filling the TX fifo incase CTS has been dropped.
203 */
204#define MCTRL_TIMEOUT (250*HZ/1000)
205
206#define DRIVER_NAME "IMX-uart"
207
Sascha Hauerdbff4e92008-07-05 10:02:45 +0200208#define UART_NR 8
209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210struct imx_port {
211 struct uart_port port;
212 struct timer_list timer;
213 unsigned int old_status;
Sascha Hauer5b802342006-05-04 14:07:42 +0100214 int txirq,rxirq,rtsirq;
Daniel Glöckner26bbb3f2009-06-11 14:36:29 +0100215 unsigned int have_rtscts:1;
Sascha Hauer38a41fd2008-07-05 10:02:46 +0200216 struct clk *clk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217};
218
219/*
220 * Handle any change of modem status signal since we were last called.
221 */
222static void imx_mctrl_check(struct imx_port *sport)
223{
224 unsigned int status, changed;
225
226 status = sport->port.ops->get_mctrl(&sport->port);
227 changed = status ^ sport->old_status;
228
229 if (changed == 0)
230 return;
231
232 sport->old_status = status;
233
234 if (changed & TIOCM_RI)
235 sport->port.icount.rng++;
236 if (changed & TIOCM_DSR)
237 sport->port.icount.dsr++;
238 if (changed & TIOCM_CAR)
239 uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
240 if (changed & TIOCM_CTS)
241 uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
242
243 wake_up_interruptible(&sport->port.info->delta_msr_wait);
244}
245
246/*
247 * This is our per-port timeout handler, for checking the
248 * modem status signals.
249 */
250static void imx_timeout(unsigned long data)
251{
252 struct imx_port *sport = (struct imx_port *)data;
253 unsigned long flags;
254
255 if (sport->port.info) {
256 spin_lock_irqsave(&sport->port.lock, flags);
257 imx_mctrl_check(sport);
258 spin_unlock_irqrestore(&sport->port.lock, flags);
259
260 mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
261 }
262}
263
264/*
265 * interrupts disabled on entry
266 */
Russell Kingb129a8c2005-08-31 10:12:14 +0100267static void imx_stop_tx(struct uart_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100270 unsigned long temp;
271
272 temp = readl(sport->port.membase + UCR1);
273 writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
276/*
277 * interrupts disabled on entry
278 */
279static void imx_stop_rx(struct uart_port *port)
280{
281 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100282 unsigned long temp;
283
284 temp = readl(sport->port.membase + UCR2);
285 writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286}
287
288/*
289 * Set the modem control timer to fire immediately.
290 */
291static void imx_enable_ms(struct uart_port *port)
292{
293 struct imx_port *sport = (struct imx_port *)port;
294
295 mod_timer(&sport->timer, jiffies);
296}
297
298static inline void imx_transmit_buffer(struct imx_port *sport)
299{
300 struct circ_buf *xmit = &sport->port.info->xmit;
301
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100302 while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 /* send xmit->buf[xmit->tail]
304 * out the port here */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100305 writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100306 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 sport->port.icount.tx++;
308 if (uart_circ_empty(xmit))
309 break;
Sascha Hauer8c0b2542007-02-05 16:10:16 -0800310 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
Fabian Godehardt977757312009-06-11 14:37:19 +0100312 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
313 uart_write_wakeup(&sport->port);
314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 if (uart_circ_empty(xmit))
Russell Kingb129a8c2005-08-31 10:12:14 +0100316 imx_stop_tx(&sport->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317}
318
319/*
320 * interrupts disabled on entry
321 */
Russell Kingb129a8c2005-08-31 10:12:14 +0100322static void imx_start_tx(struct uart_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
324 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100325 unsigned long temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100327 temp = readl(sport->port.membase + UCR1);
328 writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100330 if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
331 imx_transmit_buffer(sport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332}
333
David Howells7d12e782006-10-05 14:55:46 +0100334static irqreturn_t imx_rtsint(int irq, void *dev_id)
Sascha Hauerceca6292005-10-12 19:58:08 +0100335{
Jeff Garzik15aafa22008-02-06 01:36:20 -0800336 struct imx_port *sport = dev_id;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100337 unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS;
Sascha Hauerceca6292005-10-12 19:58:08 +0100338 unsigned long flags;
339
340 spin_lock_irqsave(&sport->port.lock, flags);
341
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100342 writel(USR1_RTSD, sport->port.membase + USR1);
Sascha Hauerceca6292005-10-12 19:58:08 +0100343 uart_handle_cts_change(&sport->port, !!val);
344 wake_up_interruptible(&sport->port.info->delta_msr_wait);
345
346 spin_unlock_irqrestore(&sport->port.lock, flags);
347 return IRQ_HANDLED;
348}
349
David Howells7d12e782006-10-05 14:55:46 +0100350static irqreturn_t imx_txint(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
Jeff Garzik15aafa22008-02-06 01:36:20 -0800352 struct imx_port *sport = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 struct circ_buf *xmit = &sport->port.info->xmit;
354 unsigned long flags;
355
356 spin_lock_irqsave(&sport->port.lock,flags);
357 if (sport->port.x_char)
358 {
359 /* Send next char */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100360 writel(sport->port.x_char, sport->port.membase + URTX0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 goto out;
362 }
363
364 if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
Russell Kingb129a8c2005-08-31 10:12:14 +0100365 imx_stop_tx(&sport->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 goto out;
367 }
368
369 imx_transmit_buffer(sport);
370
371 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
372 uart_write_wakeup(&sport->port);
373
374out:
375 spin_unlock_irqrestore(&sport->port.lock,flags);
376 return IRQ_HANDLED;
377}
378
David Howells7d12e782006-10-05 14:55:46 +0100379static irqreturn_t imx_rxint(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
381 struct imx_port *sport = dev_id;
382 unsigned int rx,flg,ignored = 0;
Takashi Iwaia88487c2008-07-16 21:54:42 +0100383 struct tty_struct *tty = sport->port.info->port.tty;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100384 unsigned long flags, temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 spin_lock_irqsave(&sport->port.lock,flags);
387
Sascha Hauer0d3c3932008-04-17 08:43:14 +0100388 while (readl(sport->port.membase + USR2) & USR2_RDR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 flg = TTY_NORMAL;
390 sport->port.icount.rx++;
391
Sascha Hauer0d3c3932008-04-17 08:43:14 +0100392 rx = readl(sport->port.membase + URXD0);
393
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100394 temp = readl(sport->port.membase + USR2);
Sascha Hauer864eeed2008-04-17 08:39:22 +0100395 if (temp & USR2_BRCD) {
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100396 writel(temp | USR2_BRCD, sport->port.membase + USR2);
Sascha Hauer864eeed2008-04-17 08:39:22 +0100397 if (uart_handle_break(&sport->port))
398 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 }
400
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100401 if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
Sascha Hauer864eeed2008-04-17 08:39:22 +0100402 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Sascha Hauer864eeed2008-04-17 08:39:22 +0100404 if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) {
405 if (rx & URXD_PRERR)
406 sport->port.icount.parity++;
407 else if (rx & URXD_FRMERR)
408 sport->port.icount.frame++;
409 if (rx & URXD_OVRRUN)
410 sport->port.icount.overrun++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Sascha Hauer864eeed2008-04-17 08:39:22 +0100412 if (rx & sport->port.ignore_status_mask) {
413 if (++ignored > 100)
414 goto out;
415 continue;
416 }
417
418 rx &= sport->port.read_status_mask;
419
420 if (rx & URXD_PRERR)
421 flg = TTY_PARITY;
422 else if (rx & URXD_FRMERR)
423 flg = TTY_FRAME;
424 if (rx & URXD_OVRRUN)
425 flg = TTY_OVERRUN;
426
427#ifdef SUPPORT_SYSRQ
428 sport->port.sysrq = 0;
429#endif
430 }
431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 tty_insert_flip_char(tty, rx, flg);
Sascha Hauer864eeed2008-04-17 08:39:22 +0100433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
435out:
436 spin_unlock_irqrestore(&sport->port.lock,flags);
437 tty_flip_buffer_push(tty);
438 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439}
440
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200441static irqreturn_t imx_int(int irq, void *dev_id)
442{
443 struct imx_port *sport = dev_id;
444 unsigned int sts;
445
446 sts = readl(sport->port.membase + USR1);
447
448 if (sts & USR1_RRDY)
449 imx_rxint(irq, dev_id);
450
451 if (sts & USR1_TRDY &&
452 readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
453 imx_txint(irq, dev_id);
454
Marc Kleine-Budde9fbe6042008-07-28 21:26:01 +0200455 if (sts & USR1_RTSD)
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200456 imx_rtsint(irq, dev_id);
457
458 return IRQ_HANDLED;
459}
460
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461/*
462 * Return TIOCSER_TEMT when transmitter is not busy.
463 */
464static unsigned int imx_tx_empty(struct uart_port *port)
465{
466 struct imx_port *sport = (struct imx_port *)port;
467
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100468 return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469}
470
Sascha Hauer0f302dc2005-08-31 21:48:47 +0100471/*
472 * We have a modem side uart, so the meanings of RTS and CTS are inverted.
473 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474static unsigned int imx_get_mctrl(struct uart_port *port)
475{
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100476 struct imx_port *sport = (struct imx_port *)port;
477 unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
Sascha Hauer0f302dc2005-08-31 21:48:47 +0100478
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100479 if (readl(sport->port.membase + USR1) & USR1_RTSS)
480 tmp |= TIOCM_CTS;
Sascha Hauer0f302dc2005-08-31 21:48:47 +0100481
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100482 if (readl(sport->port.membase + UCR2) & UCR2_CTS)
483 tmp |= TIOCM_RTS;
Sascha Hauer0f302dc2005-08-31 21:48:47 +0100484
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100485 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486}
487
488static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
489{
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100490 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100491 unsigned long temp;
492
493 temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
Sascha Hauer0f302dc2005-08-31 21:48:47 +0100494
Oskar Schirmerd3810cd2009-06-11 14:35:01 +0100495 if (mctrl & TIOCM_RTS)
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100496 temp |= UCR2_CTS;
497
498 writel(temp, sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
501/*
502 * Interrupts always disabled.
503 */
504static void imx_break_ctl(struct uart_port *port, int break_state)
505{
506 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100507 unsigned long flags, temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 spin_lock_irqsave(&sport->port.lock, flags);
510
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100511 temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 if ( break_state != 0 )
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100514 temp |= UCR1_SNDBRK;
515
516 writel(temp, sport->port.membase + UCR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
518 spin_unlock_irqrestore(&sport->port.lock, flags);
519}
520
521#define TXTL 2 /* reset default */
522#define RXTL 1 /* reset default */
523
Sascha Hauer587897f2005-04-29 22:46:40 +0100524static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
525{
526 unsigned int val;
527 unsigned int ufcr_rfdiv;
528
529 /* set receiver / transmitter trigger level.
530 * RFDIV is set such way to satisfy requested uartclk value
531 */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100532 val = TXTL << 10 | RXTL;
Sascha Hauer38a41fd2008-07-05 10:02:46 +0200533 ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2)
534 / sport->port.uartclk;
Sascha Hauer587897f2005-04-29 22:46:40 +0100535
536 if(!ufcr_rfdiv)
537 ufcr_rfdiv = 1;
538
539 if(ufcr_rfdiv >= 7)
540 ufcr_rfdiv = 6;
541 else
542 ufcr_rfdiv = 6 - ufcr_rfdiv;
543
544 val |= UFCR_RFDIV & (ufcr_rfdiv << 7);
545
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100546 writel(val, sport->port.membase + UFCR);
Sascha Hauer587897f2005-04-29 22:46:40 +0100547
548 return 0;
549}
550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551static int imx_startup(struct uart_port *port)
552{
553 struct imx_port *sport = (struct imx_port *)port;
554 int retval;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100555 unsigned long flags, temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Sascha Hauer587897f2005-04-29 22:46:40 +0100557 imx_setup_ufcr(sport, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 /* disable the DREN bit (Data Ready interrupt enable) before
560 * requesting IRQs
561 */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100562 temp = readl(sport->port.membase + UCR4);
563 writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565 /*
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200566 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
567 * chips only have one interrupt.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 */
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200569 if (sport->txirq > 0) {
570 retval = request_irq(sport->rxirq, imx_rxint, 0,
571 DRIVER_NAME, sport);
572 if (retval)
573 goto error_out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200575 retval = request_irq(sport->txirq, imx_txint, 0,
576 DRIVER_NAME, sport);
577 if (retval)
578 goto error_out2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200580 retval = request_irq(sport->rtsirq, imx_rtsint,
581 (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
Pavel Pisad7ea10d2007-02-05 16:10:20 -0800582 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200583 DRIVER_NAME, sport);
584 if (retval)
585 goto error_out3;
586 } else {
587 retval = request_irq(sport->port.irq, imx_int, 0,
588 DRIVER_NAME, sport);
589 if (retval) {
590 free_irq(sport->port.irq, sport);
591 goto error_out1;
592 }
593 }
Sascha Hauerceca6292005-10-12 19:58:08 +0100594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 /*
596 * Finally, clear and enable interrupts
597 */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100598 writel(USR1_RTSD, sport->port.membase + USR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100600 temp = readl(sport->port.membase + UCR1);
Sascha Hauer789d5252008-04-17 08:44:47 +0100601 temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100602 writel(temp, sport->port.membase + UCR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100604 temp = readl(sport->port.membase + UCR2);
605 temp |= (UCR2_RXEN | UCR2_TXEN);
606 writel(temp, sport->port.membase + UCR2);
607
Marc Kleine-Budde44118052008-07-28 12:10:34 +0200608#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
609 temp = readl(sport->port.membase + UCR3);
610 temp |= UCR3_RXDMUXSEL;
611 writel(temp, sport->port.membase + UCR3);
612#endif
613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 /*
615 * Enable modem status interrupts
616 */
617 spin_lock_irqsave(&sport->port.lock,flags);
618 imx_enable_ms(&sport->port);
619 spin_unlock_irqrestore(&sport->port.lock,flags);
620
621 return 0;
622
Sascha Hauerceca6292005-10-12 19:58:08 +0100623error_out3:
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200624 if (sport->txirq)
625 free_irq(sport->txirq, sport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626error_out2:
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200627 if (sport->rxirq)
628 free_irq(sport->rxirq, sport);
Sascha Hauer86371d02005-10-10 10:17:42 +0100629error_out1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return retval;
631}
632
633static void imx_shutdown(struct uart_port *port)
634{
635 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100636 unsigned long temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Fabian Godehardt2e146392009-06-11 14:38:38 +0100638 temp = readl(sport->port.membase + UCR2);
639 temp &= ~(UCR2_TXEN);
640 writel(temp, sport->port.membase + UCR2);
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 /*
643 * Stop our timer.
644 */
645 del_timer_sync(&sport->timer);
646
647 /*
648 * Free the interrupts
649 */
Sascha Hauere3d13ff2008-07-05 10:02:48 +0200650 if (sport->txirq > 0) {
651 free_irq(sport->rtsirq, sport);
652 free_irq(sport->txirq, sport);
653 free_irq(sport->rxirq, sport);
654 } else
655 free_irq(sport->port.irq, sport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
657 /*
658 * Disable all interrupts, port and break condition.
659 */
660
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100661 temp = readl(sport->port.membase + UCR1);
662 temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
663 writel(temp, sport->port.membase + UCR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664}
665
666static void
Alan Cox606d0992006-12-08 02:38:45 -0800667imx_set_termios(struct uart_port *port, struct ktermios *termios,
668 struct ktermios *old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
670 struct imx_port *sport = (struct imx_port *)port;
671 unsigned long flags;
672 unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
673 unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
Oskar Schirmer534fca02009-06-11 14:52:23 +0100674 unsigned int div, ufcr;
675 unsigned long num, denom;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
677 /*
678 * If we don't support modem control lines, don't allow
679 * these to be set.
680 */
681 if (0) {
682 termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
683 termios->c_cflag |= CLOCAL;
684 }
685
686 /*
687 * We only support CS7 and CS8.
688 */
689 while ((termios->c_cflag & CSIZE) != CS7 &&
690 (termios->c_cflag & CSIZE) != CS8) {
691 termios->c_cflag &= ~CSIZE;
692 termios->c_cflag |= old_csize;
693 old_csize = CS8;
694 }
695
696 if ((termios->c_cflag & CSIZE) == CS8)
697 ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
698 else
699 ucr2 = UCR2_SRST | UCR2_IRTS;
700
701 if (termios->c_cflag & CRTSCTS) {
Sascha Hauer5b802342006-05-04 14:07:42 +0100702 if( sport->have_rtscts ) {
703 ucr2 &= ~UCR2_IRTS;
704 ucr2 |= UCR2_CTSC;
705 } else {
706 termios->c_cflag &= ~CRTSCTS;
707 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
709
710 if (termios->c_cflag & CSTOPB)
711 ucr2 |= UCR2_STPB;
712 if (termios->c_cflag & PARENB) {
713 ucr2 |= UCR2_PREN;
Matt Reimer3261e362006-01-13 20:51:44 +0000714 if (termios->c_cflag & PARODD)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 ucr2 |= UCR2_PROE;
716 }
717
718 /*
719 * Ask the core to calculate the divisor for us.
720 */
Sascha Hauer036bb152008-07-05 10:02:44 +0200721 baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 quot = uart_get_divisor(port, baud);
723
724 spin_lock_irqsave(&sport->port.lock, flags);
725
726 sport->port.read_status_mask = 0;
727 if (termios->c_iflag & INPCK)
728 sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
729 if (termios->c_iflag & (BRKINT | PARMRK))
730 sport->port.read_status_mask |= URXD_BRK;
731
732 /*
733 * Characters to ignore
734 */
735 sport->port.ignore_status_mask = 0;
736 if (termios->c_iflag & IGNPAR)
737 sport->port.ignore_status_mask |= URXD_PRERR;
738 if (termios->c_iflag & IGNBRK) {
739 sport->port.ignore_status_mask |= URXD_BRK;
740 /*
741 * If we're ignoring parity and break indicators,
742 * ignore overruns too (for real raw support).
743 */
744 if (termios->c_iflag & IGNPAR)
745 sport->port.ignore_status_mask |= URXD_OVRRUN;
746 }
747
748 del_timer_sync(&sport->timer);
749
750 /*
751 * Update the per-port timeout.
752 */
753 uart_update_timeout(port, termios->c_cflag, baud);
754
755 /*
756 * disable interrupts and drain transmitter
757 */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100758 old_ucr1 = readl(sport->port.membase + UCR1);
759 writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
760 sport->port.membase + UCR1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100762 while ( !(readl(sport->port.membase + USR2) & USR2_TXDC))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 barrier();
764
765 /* then, disable everything */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100766 old_txrxen = readl(sport->port.membase + UCR2);
767 writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN),
768 sport->port.membase + UCR2);
769 old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Sascha Hauer036bb152008-07-05 10:02:44 +0200771 div = sport->port.uartclk / (baud * 16);
772 if (div > 7)
773 div = 7;
774 if (!div)
775 div = 1;
776
Oskar Schirmer534fca02009-06-11 14:52:23 +0100777 rational_best_approximation(16 * div * baud, sport->port.uartclk,
778 1 << 16, 1 << 16, &num, &denom);
Sascha Hauer036bb152008-07-05 10:02:44 +0200779
Oskar Schirmer534fca02009-06-11 14:52:23 +0100780 num -= 1;
781 denom -= 1;
Sascha Hauer036bb152008-07-05 10:02:44 +0200782
783 ufcr = readl(sport->port.membase + UFCR);
784 ufcr = (ufcr & (~UFCR_RFDIV)) |
785 (div << 7);
786 writel(ufcr, sport->port.membase + UFCR);
787
Oskar Schirmer534fca02009-06-11 14:52:23 +0100788 writel(num, sport->port.membase + UBIR);
789 writel(denom, sport->port.membase + UBMR);
790
Sascha Hauer036bb152008-07-05 10:02:44 +0200791#ifdef ONEMS
792 writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
793#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100795 writel(old_ucr1, sport->port.membase + UCR1);
796
797 /* set the parity, stop bits and data size */
798 writel(ucr2 | old_txrxen, sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
801 imx_enable_ms(&sport->port);
802
803 spin_unlock_irqrestore(&sport->port.lock, flags);
804}
805
806static const char *imx_type(struct uart_port *port)
807{
808 struct imx_port *sport = (struct imx_port *)port;
809
810 return sport->port.type == PORT_IMX ? "IMX" : NULL;
811}
812
813/*
814 * Release the memory region(s) being used by 'port'.
815 */
816static void imx_release_port(struct uart_port *port)
817{
Sascha Hauer3d454442008-04-17 08:47:32 +0100818 struct platform_device *pdev = to_platform_device(port->dev);
819 struct resource *mmres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Sascha Hauer3d454442008-04-17 08:47:32 +0100821 mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
822 release_mem_region(mmres->start, mmres->end - mmres->start + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823}
824
825/*
826 * Request the memory region(s) being used by 'port'.
827 */
828static int imx_request_port(struct uart_port *port)
829{
Sascha Hauer3d454442008-04-17 08:47:32 +0100830 struct platform_device *pdev = to_platform_device(port->dev);
831 struct resource *mmres;
832 void *ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Sascha Hauer3d454442008-04-17 08:47:32 +0100834 mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
835 if (!mmres)
836 return -ENODEV;
837
838 ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1,
839 "imx-uart");
840
841 return ret ? 0 : -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842}
843
844/*
845 * Configure/autoconfigure the port.
846 */
847static void imx_config_port(struct uart_port *port, int flags)
848{
849 struct imx_port *sport = (struct imx_port *)port;
850
851 if (flags & UART_CONFIG_TYPE &&
852 imx_request_port(&sport->port) == 0)
853 sport->port.type = PORT_IMX;
854}
855
856/*
857 * Verify the new serial_struct (for TIOCSSERIAL).
858 * The only change we allow are to the flags and type, and
859 * even then only between PORT_IMX and PORT_UNKNOWN
860 */
861static int
862imx_verify_port(struct uart_port *port, struct serial_struct *ser)
863{
864 struct imx_port *sport = (struct imx_port *)port;
865 int ret = 0;
866
867 if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX)
868 ret = -EINVAL;
869 if (sport->port.irq != ser->irq)
870 ret = -EINVAL;
871 if (ser->io_type != UPIO_MEM)
872 ret = -EINVAL;
873 if (sport->port.uartclk / 16 != ser->baud_base)
874 ret = -EINVAL;
875 if ((void *)sport->port.mapbase != ser->iomem_base)
876 ret = -EINVAL;
877 if (sport->port.iobase != ser->port)
878 ret = -EINVAL;
879 if (ser->hub6 != 0)
880 ret = -EINVAL;
881 return ret;
882}
883
884static struct uart_ops imx_pops = {
885 .tx_empty = imx_tx_empty,
886 .set_mctrl = imx_set_mctrl,
887 .get_mctrl = imx_get_mctrl,
888 .stop_tx = imx_stop_tx,
889 .start_tx = imx_start_tx,
890 .stop_rx = imx_stop_rx,
891 .enable_ms = imx_enable_ms,
892 .break_ctl = imx_break_ctl,
893 .startup = imx_startup,
894 .shutdown = imx_shutdown,
895 .set_termios = imx_set_termios,
896 .type = imx_type,
897 .release_port = imx_release_port,
898 .request_port = imx_request_port,
899 .config_port = imx_config_port,
900 .verify_port = imx_verify_port,
901};
902
Sascha Hauerdbff4e92008-07-05 10:02:45 +0200903static struct imx_port *imx_ports[UART_NR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905#ifdef CONFIG_SERIAL_IMX_CONSOLE
Russell Kingd3587882006-03-20 20:00:09 +0000906static void imx_console_putchar(struct uart_port *port, int ch)
907{
908 struct imx_port *sport = (struct imx_port *)port;
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100909
910 while (readl(sport->port.membase + UTS) & UTS_TXFULL)
Russell Kingd3587882006-03-20 20:00:09 +0000911 barrier();
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100912
913 writel(ch, sport->port.membase + URTX0);
Russell Kingd3587882006-03-20 20:00:09 +0000914}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916/*
917 * Interrupts are disabled on entering
918 */
919static void
920imx_console_write(struct console *co, const char *s, unsigned int count)
921{
Sascha Hauerdbff4e92008-07-05 10:02:45 +0200922 struct imx_port *sport = imx_ports[co->index];
Russell Kingd3587882006-03-20 20:00:09 +0000923 unsigned int old_ucr1, old_ucr2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
925 /*
926 * First, save UCR1/2 and then disable interrupts
927 */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100928 old_ucr1 = readl(sport->port.membase + UCR1);
929 old_ucr2 = readl(sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100931 writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) &
932 ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
933 sport->port.membase + UCR1);
934
935 writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
Russell Kingd3587882006-03-20 20:00:09 +0000937 uart_console_write(&sport->port, s, count, imx_console_putchar);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
939 /*
940 * Finally, wait for transmitter to become empty
941 * and restore UCR1/2
942 */
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100943 while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100945 writel(old_ucr1, sport->port.membase + UCR1);
946 writel(old_ucr2, sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947}
948
949/*
950 * If the port was already initialised (eg, by a boot loader),
951 * try to determine the current setup.
952 */
953static void __init
954imx_console_get_options(struct imx_port *sport, int *baud,
955 int *parity, int *bits)
956{
Sascha Hauer587897f2005-04-29 22:46:40 +0100957
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100958 if ( readl(sport->port.membase + UCR1) | UCR1_UARTEN ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 /* ok, the port was enabled */
960 unsigned int ucr2, ubir,ubmr, uartclk;
Sascha Hauer587897f2005-04-29 22:46:40 +0100961 unsigned int baud_raw;
962 unsigned int ucfr_rfdiv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100964 ucr2 = readl(sport->port.membase + UCR2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 *parity = 'n';
967 if (ucr2 & UCR2_PREN) {
968 if (ucr2 & UCR2_PROE)
969 *parity = 'o';
970 else
971 *parity = 'e';
972 }
973
974 if (ucr2 & UCR2_WS)
975 *bits = 8;
976 else
977 *bits = 7;
978
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100979 ubir = readl(sport->port.membase + UBIR) & 0xffff;
980 ubmr = readl(sport->port.membase + UBMR) & 0xffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
Sascha Hauerff4bfb22007-04-26 08:26:13 +0100982 ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7;
Sascha Hauer587897f2005-04-29 22:46:40 +0100983 if (ucfr_rfdiv == 6)
984 ucfr_rfdiv = 7;
985 else
986 ucfr_rfdiv = 6 - ucfr_rfdiv;
987
Sascha Hauer38a41fd2008-07-05 10:02:46 +0200988 uartclk = clk_get_rate(sport->clk);
Sascha Hauer587897f2005-04-29 22:46:40 +0100989 uartclk /= ucfr_rfdiv;
990
991 { /*
992 * The next code provides exact computation of
993 * baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1))
994 * without need of float support or long long division,
995 * which would be required to prevent 32bit arithmetic overflow
996 */
997 unsigned int mul = ubir + 1;
998 unsigned int div = 16 * (ubmr + 1);
999 unsigned int rem = uartclk % div;
1000
1001 baud_raw = (uartclk / div) * mul;
1002 baud_raw += (rem * mul + div / 2) / div;
1003 *baud = (baud_raw + 50) / 100 * 100;
1004 }
1005
1006 if(*baud != baud_raw)
1007 printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
1008 baud_raw, *baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 }
1010}
1011
1012static int __init
1013imx_console_setup(struct console *co, char *options)
1014{
1015 struct imx_port *sport;
1016 int baud = 9600;
1017 int bits = 8;
1018 int parity = 'n';
1019 int flow = 'n';
1020
1021 /*
1022 * Check whether an invalid uart number has been specified, and
1023 * if so, search for the first available port that does have
1024 * console support.
1025 */
1026 if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
1027 co->index = 0;
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001028 sport = imx_ports[co->index];
Eric Lammertse76afc42009-05-19 20:53:20 -04001029 if(sport == NULL)
1030 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
1032 if (options)
1033 uart_parse_options(options, &baud, &parity, &bits, &flow);
1034 else
1035 imx_console_get_options(sport, &baud, &parity, &bits);
1036
Sascha Hauer587897f2005-04-29 22:46:40 +01001037 imx_setup_ufcr(sport, 0);
1038
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 return uart_set_options(&sport->port, co, baud, parity, bits, flow);
1040}
1041
Vincent Sanders9f4426d2005-10-01 22:56:34 +01001042static struct uart_driver imx_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043static struct console imx_console = {
Sascha Hauere3d13ff2008-07-05 10:02:48 +02001044 .name = DEV_NAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 .write = imx_console_write,
1046 .device = uart_console_device,
1047 .setup = imx_console_setup,
1048 .flags = CON_PRINTBUFFER,
1049 .index = -1,
1050 .data = &imx_reg,
1051};
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053#define IMX_CONSOLE &imx_console
1054#else
1055#define IMX_CONSOLE NULL
1056#endif
1057
1058static struct uart_driver imx_reg = {
1059 .owner = THIS_MODULE,
1060 .driver_name = DRIVER_NAME,
Sascha Hauere3d13ff2008-07-05 10:02:48 +02001061 .dev_name = DEV_NAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 .major = SERIAL_IMX_MAJOR,
1063 .minor = MINOR_START,
1064 .nr = ARRAY_SIZE(imx_ports),
1065 .cons = IMX_CONSOLE,
1066};
1067
Russell King3ae5eae2005-11-09 22:32:44 +00001068static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069{
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001070 struct imx_port *sport = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001072 if (sport)
1073 uart_suspend_port(&imx_reg, &sport->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001075 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076}
1077
Russell King3ae5eae2005-11-09 22:32:44 +00001078static int serial_imx_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001080 struct imx_port *sport = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001082 if (sport)
1083 uart_resume_port(&imx_reg, &sport->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001085 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086}
1087
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001088static int serial_imx_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089{
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001090 struct imx_port *sport;
Sascha Hauer5b802342006-05-04 14:07:42 +01001091 struct imxuart_platform_data *pdata;
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001092 void __iomem *base;
1093 int ret = 0;
1094 struct resource *res;
Sascha Hauer5b802342006-05-04 14:07:42 +01001095
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001096 sport = kzalloc(sizeof(*sport), GFP_KERNEL);
1097 if (!sport)
1098 return -ENOMEM;
1099
1100 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1101 if (!res) {
1102 ret = -ENODEV;
1103 goto free;
1104 }
1105
1106 base = ioremap(res->start, PAGE_SIZE);
1107 if (!base) {
1108 ret = -ENOMEM;
1109 goto free;
1110 }
1111
1112 sport->port.dev = &pdev->dev;
1113 sport->port.mapbase = res->start;
1114 sport->port.membase = base;
1115 sport->port.type = PORT_IMX,
1116 sport->port.iotype = UPIO_MEM;
1117 sport->port.irq = platform_get_irq(pdev, 0);
1118 sport->rxirq = platform_get_irq(pdev, 0);
1119 sport->txirq = platform_get_irq(pdev, 1);
1120 sport->rtsirq = platform_get_irq(pdev, 2);
1121 sport->port.fifosize = 32;
1122 sport->port.ops = &imx_pops;
1123 sport->port.flags = UPF_BOOT_AUTOCONF;
1124 sport->port.line = pdev->id;
1125 init_timer(&sport->timer);
1126 sport->timer.function = imx_timeout;
1127 sport->timer.data = (unsigned long)sport;
Sascha Hauer38a41fd2008-07-05 10:02:46 +02001128
Sascha Hauere65fb002009-02-16 14:29:10 +01001129 sport->clk = clk_get(&pdev->dev, "uart");
Sascha Hauer38a41fd2008-07-05 10:02:46 +02001130 if (IS_ERR(sport->clk)) {
1131 ret = PTR_ERR(sport->clk);
1132 goto unmap;
1133 }
1134 clk_enable(sport->clk);
1135
1136 sport->port.uartclk = clk_get_rate(sport->clk);
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001137
1138 imx_ports[pdev->id] = sport;
Sascha Hauer5b802342006-05-04 14:07:42 +01001139
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001140 pdata = pdev->dev.platform_data;
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001141 if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001142 sport->have_rtscts = 1;
Sascha Hauer5b802342006-05-04 14:07:42 +01001143
Darius Augulisc45e7d72008-09-02 10:19:29 +02001144 if (pdata->init) {
1145 ret = pdata->init(pdev);
1146 if (ret)
1147 goto clkput;
1148 }
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001149
Daniel Glöckner9f322ad2009-06-11 14:39:21 +01001150 ret = uart_add_one_port(&imx_reg, &sport->port);
1151 if (ret)
1152 goto deinit;
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001153 platform_set_drvdata(pdev, &sport->port);
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 return 0;
Daniel Glöckner9f322ad2009-06-11 14:39:21 +01001156deinit:
1157 if (pdata->exit)
1158 pdata->exit(pdev);
Darius Augulisc45e7d72008-09-02 10:19:29 +02001159clkput:
1160 clk_put(sport->clk);
1161 clk_disable(sport->clk);
Sascha Hauer38a41fd2008-07-05 10:02:46 +02001162unmap:
1163 iounmap(sport->port.membase);
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001164free:
1165 kfree(sport);
1166
1167 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168}
1169
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001170static int serial_imx_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171{
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001172 struct imxuart_platform_data *pdata;
1173 struct imx_port *sport = platform_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001175 pdata = pdev->dev.platform_data;
1176
1177 platform_set_drvdata(pdev, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
Sascha Hauer38a41fd2008-07-05 10:02:46 +02001179 if (sport) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 uart_remove_one_port(&imx_reg, &sport->port);
Sascha Hauer38a41fd2008-07-05 10:02:46 +02001181 clk_put(sport->clk);
1182 }
1183
1184 clk_disable(sport->clk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185
Sascha Hauer2582d8c2008-07-05 10:02:45 +02001186 if (pdata->exit)
1187 pdata->exit(pdev);
1188
Sascha Hauerdbff4e92008-07-05 10:02:45 +02001189 iounmap(sport->port.membase);
1190 kfree(sport);
1191
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 return 0;
1193}
1194
Russell King3ae5eae2005-11-09 22:32:44 +00001195static struct platform_driver serial_imx_driver = {
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001196 .probe = serial_imx_probe,
1197 .remove = serial_imx_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
1199 .suspend = serial_imx_suspend,
1200 .resume = serial_imx_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00001201 .driver = {
Oskar Schirmerd3810cd2009-06-11 14:35:01 +01001202 .name = "imx-uart",
Kay Sieverse169c132008-04-15 14:34:35 -07001203 .owner = THIS_MODULE,
Russell King3ae5eae2005-11-09 22:32:44 +00001204 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205};
1206
1207static int __init imx_serial_init(void)
1208{
1209 int ret;
1210
1211 printk(KERN_INFO "Serial: IMX driver\n");
1212
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 ret = uart_register_driver(&imx_reg);
1214 if (ret)
1215 return ret;
1216
Russell King3ae5eae2005-11-09 22:32:44 +00001217 ret = platform_driver_register(&serial_imx_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 if (ret != 0)
1219 uart_unregister_driver(&imx_reg);
1220
1221 return 0;
1222}
1223
1224static void __exit imx_serial_exit(void)
1225{
Russell Kingc889b892005-11-21 17:05:21 +00001226 platform_driver_unregister(&serial_imx_driver);
Sascha Hauer4b300c32007-07-17 13:35:46 +01001227 uart_unregister_driver(&imx_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228}
1229
1230module_init(imx_serial_init);
1231module_exit(imx_serial_exit);
1232
1233MODULE_AUTHOR("Sascha Hauer");
1234MODULE_DESCRIPTION("IMX generic serial port driver");
1235MODULE_LICENSE("GPL");
Kay Sieverse169c132008-04-15 14:34:35 -07001236MODULE_ALIAS("platform:imx-uart");