blob: 06d72713fb9c6d3f19d1ab39995dca15bdd2f5eb [file] [log] [blame]
Sascha Hauer47d37d62011-01-11 15:54:54 +01001/*
2 * Freescale STMP37XX/STMP378X Application UART driver
3 *
4 * Author: dmitry pervushin <dimka@embeddedalley.com>
5 *
6 * Copyright 2008-2010 Freescale Semiconductor, Inc.
7 * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
8 *
9 * The code contained herein is licensed under the GNU General Public
10 * License. You may obtain a copy of the GNU General Public License
11 * Version 2 or later at the following locations:
12 *
13 * http://www.opensource.org/licenses/gpl-license.html
14 * http://www.gnu.org/copyleft/gpl.html
15 */
16
17#include <linux/kernel.h>
Sascha Hauer47d37d62011-01-11 15:54:54 +010018#include <linux/errno.h>
19#include <linux/init.h>
20#include <linux/console.h>
21#include <linux/interrupt.h>
22#include <linux/module.h>
23#include <linux/slab.h>
24#include <linux/wait.h>
25#include <linux/tty.h>
26#include <linux/tty_driver.h>
27#include <linux/tty_flip.h>
28#include <linux/serial.h>
29#include <linux/serial_core.h>
30#include <linux/platform_device.h>
31#include <linux/device.h>
32#include <linux/clk.h>
33#include <linux/delay.h>
34#include <linux/io.h>
Shawn Guo2e174c32012-05-06 22:54:26 +080035#include <linux/pinctrl/consumer.h>
Fabio Estevam1ea66072012-06-18 10:06:09 -030036#include <linux/of_device.h>
Sascha Hauer47d37d62011-01-11 15:54:54 +010037
38#include <asm/cacheflush.h>
39
40#define MXS_AUART_PORTS 5
41
42#define AUART_CTRL0 0x00000000
43#define AUART_CTRL0_SET 0x00000004
44#define AUART_CTRL0_CLR 0x00000008
45#define AUART_CTRL0_TOG 0x0000000c
46#define AUART_CTRL1 0x00000010
47#define AUART_CTRL1_SET 0x00000014
48#define AUART_CTRL1_CLR 0x00000018
49#define AUART_CTRL1_TOG 0x0000001c
50#define AUART_CTRL2 0x00000020
51#define AUART_CTRL2_SET 0x00000024
52#define AUART_CTRL2_CLR 0x00000028
53#define AUART_CTRL2_TOG 0x0000002c
54#define AUART_LINECTRL 0x00000030
55#define AUART_LINECTRL_SET 0x00000034
56#define AUART_LINECTRL_CLR 0x00000038
57#define AUART_LINECTRL_TOG 0x0000003c
58#define AUART_LINECTRL2 0x00000040
59#define AUART_LINECTRL2_SET 0x00000044
60#define AUART_LINECTRL2_CLR 0x00000048
61#define AUART_LINECTRL2_TOG 0x0000004c
62#define AUART_INTR 0x00000050
63#define AUART_INTR_SET 0x00000054
64#define AUART_INTR_CLR 0x00000058
65#define AUART_INTR_TOG 0x0000005c
66#define AUART_DATA 0x00000060
67#define AUART_STAT 0x00000070
68#define AUART_DEBUG 0x00000080
69#define AUART_VERSION 0x00000090
70#define AUART_AUTOBAUD 0x000000a0
71
72#define AUART_CTRL0_SFTRST (1 << 31)
73#define AUART_CTRL0_CLKGATE (1 << 30)
74
75#define AUART_CTRL2_CTSEN (1 << 15)
Huang Shijie00592022012-08-08 10:37:59 +080076#define AUART_CTRL2_RTSEN (1 << 14)
Sascha Hauer47d37d62011-01-11 15:54:54 +010077#define AUART_CTRL2_RTS (1 << 11)
78#define AUART_CTRL2_RXE (1 << 9)
79#define AUART_CTRL2_TXE (1 << 8)
80#define AUART_CTRL2_UARTEN (1 << 0)
81
82#define AUART_LINECTRL_BAUD_DIVINT_SHIFT 16
83#define AUART_LINECTRL_BAUD_DIVINT_MASK 0xffff0000
84#define AUART_LINECTRL_BAUD_DIVINT(v) (((v) & 0xffff) << 16)
85#define AUART_LINECTRL_BAUD_DIVFRAC_SHIFT 8
86#define AUART_LINECTRL_BAUD_DIVFRAC_MASK 0x00003f00
87#define AUART_LINECTRL_BAUD_DIVFRAC(v) (((v) & 0x3f) << 8)
88#define AUART_LINECTRL_WLEN_MASK 0x00000060
89#define AUART_LINECTRL_WLEN(v) (((v) & 0x3) << 5)
90#define AUART_LINECTRL_FEN (1 << 4)
91#define AUART_LINECTRL_STP2 (1 << 3)
92#define AUART_LINECTRL_EPS (1 << 2)
93#define AUART_LINECTRL_PEN (1 << 1)
94#define AUART_LINECTRL_BRK (1 << 0)
95
96#define AUART_INTR_RTIEN (1 << 22)
97#define AUART_INTR_TXIEN (1 << 21)
98#define AUART_INTR_RXIEN (1 << 20)
99#define AUART_INTR_CTSMIEN (1 << 17)
100#define AUART_INTR_RTIS (1 << 6)
101#define AUART_INTR_TXIS (1 << 5)
102#define AUART_INTR_RXIS (1 << 4)
103#define AUART_INTR_CTSMIS (1 << 1)
104
105#define AUART_STAT_BUSY (1 << 29)
106#define AUART_STAT_CTS (1 << 28)
107#define AUART_STAT_TXFE (1 << 27)
108#define AUART_STAT_TXFF (1 << 25)
109#define AUART_STAT_RXFE (1 << 24)
110#define AUART_STAT_OERR (1 << 19)
111#define AUART_STAT_BERR (1 << 18)
112#define AUART_STAT_PERR (1 << 17)
113#define AUART_STAT_FERR (1 << 16)
114
115static struct uart_driver auart_driver;
116
Huang Shijief4b1f03b2012-11-16 16:03:52 +0800117enum mxs_auart_type {
118 IMX23_AUART,
119 IMX28_AUART,
120};
121
Sascha Hauer47d37d62011-01-11 15:54:54 +0100122struct mxs_auart_port {
123 struct uart_port port;
124
125 unsigned int flags;
126 unsigned int ctrl;
Huang Shijief4b1f03b2012-11-16 16:03:52 +0800127 enum mxs_auart_type devtype;
Sascha Hauer47d37d62011-01-11 15:54:54 +0100128
129 unsigned int irq;
130
131 struct clk *clk;
132 struct device *dev;
133};
134
Huang Shijief4b1f03b2012-11-16 16:03:52 +0800135static struct platform_device_id mxs_auart_devtype[] = {
136 { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART },
137 { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART },
138 { /* sentinel */ }
139};
140MODULE_DEVICE_TABLE(platform, mxs_auart_devtype);
141
142static struct of_device_id mxs_auart_dt_ids[] = {
143 {
144 .compatible = "fsl,imx28-auart",
145 .data = &mxs_auart_devtype[IMX28_AUART]
146 }, {
147 .compatible = "fsl,imx23-auart",
148 .data = &mxs_auart_devtype[IMX23_AUART]
149 }, { /* sentinel */ }
150};
151MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids);
152
153static inline int is_imx28_auart(struct mxs_auart_port *s)
154{
155 return s->devtype == IMX28_AUART;
156}
157
Sascha Hauer47d37d62011-01-11 15:54:54 +0100158static void mxs_auart_stop_tx(struct uart_port *u);
159
160#define to_auart_port(u) container_of(u, struct mxs_auart_port, port)
161
162static inline void mxs_auart_tx_chars(struct mxs_auart_port *s)
163{
164 struct circ_buf *xmit = &s->port.state->xmit;
165
166 while (!(readl(s->port.membase + AUART_STAT) &
167 AUART_STAT_TXFF)) {
168 if (s->port.x_char) {
169 s->port.icount.tx++;
170 writel(s->port.x_char,
171 s->port.membase + AUART_DATA);
172 s->port.x_char = 0;
173 continue;
174 }
175 if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
176 s->port.icount.tx++;
177 writel(xmit->buf[xmit->tail],
178 s->port.membase + AUART_DATA);
179 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100180 } else
181 break;
182 }
Uwe Kleine-Königd0758a22011-11-22 14:22:56 +0100183 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
184 uart_write_wakeup(&s->port);
185
Sascha Hauer47d37d62011-01-11 15:54:54 +0100186 if (uart_circ_empty(&(s->port.state->xmit)))
187 writel(AUART_INTR_TXIEN,
188 s->port.membase + AUART_INTR_CLR);
189 else
190 writel(AUART_INTR_TXIEN,
191 s->port.membase + AUART_INTR_SET);
192
193 if (uart_tx_stopped(&s->port))
194 mxs_auart_stop_tx(&s->port);
195}
196
197static void mxs_auart_rx_char(struct mxs_auart_port *s)
198{
199 int flag;
200 u32 stat;
201 u8 c;
202
203 c = readl(s->port.membase + AUART_DATA);
204 stat = readl(s->port.membase + AUART_STAT);
205
206 flag = TTY_NORMAL;
207 s->port.icount.rx++;
208
209 if (stat & AUART_STAT_BERR) {
210 s->port.icount.brk++;
211 if (uart_handle_break(&s->port))
212 goto out;
213 } else if (stat & AUART_STAT_PERR) {
214 s->port.icount.parity++;
215 } else if (stat & AUART_STAT_FERR) {
216 s->port.icount.frame++;
217 }
218
219 /*
220 * Mask off conditions which should be ingored.
221 */
222 stat &= s->port.read_status_mask;
223
224 if (stat & AUART_STAT_BERR) {
225 flag = TTY_BREAK;
226 } else if (stat & AUART_STAT_PERR)
227 flag = TTY_PARITY;
228 else if (stat & AUART_STAT_FERR)
229 flag = TTY_FRAME;
230
231 if (stat & AUART_STAT_OERR)
232 s->port.icount.overrun++;
233
234 if (uart_handle_sysrq_char(&s->port, c))
235 goto out;
236
237 uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag);
238out:
239 writel(stat, s->port.membase + AUART_STAT);
240}
241
242static void mxs_auart_rx_chars(struct mxs_auart_port *s)
243{
244 struct tty_struct *tty = s->port.state->port.tty;
245 u32 stat = 0;
246
247 for (;;) {
248 stat = readl(s->port.membase + AUART_STAT);
249 if (stat & AUART_STAT_RXFE)
250 break;
251 mxs_auart_rx_char(s);
252 }
253
254 writel(stat, s->port.membase + AUART_STAT);
255 tty_flip_buffer_push(tty);
256}
257
258static int mxs_auart_request_port(struct uart_port *u)
259{
260 return 0;
261}
262
263static int mxs_auart_verify_port(struct uart_port *u,
264 struct serial_struct *ser)
265{
266 if (u->type != PORT_UNKNOWN && u->type != PORT_IMX)
267 return -EINVAL;
268 return 0;
269}
270
271static void mxs_auart_config_port(struct uart_port *u, int flags)
272{
273}
274
275static const char *mxs_auart_type(struct uart_port *u)
276{
277 struct mxs_auart_port *s = to_auart_port(u);
278
279 return dev_name(s->dev);
280}
281
282static void mxs_auart_release_port(struct uart_port *u)
283{
284}
285
286static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
287{
288 struct mxs_auart_port *s = to_auart_port(u);
289
290 u32 ctrl = readl(u->membase + AUART_CTRL2);
291
Huang Shijie00592022012-08-08 10:37:59 +0800292 ctrl &= ~AUART_CTRL2_RTSEN;
293 if (mctrl & TIOCM_RTS) {
Huang Shijief21ec3d2012-08-22 22:13:36 -0400294 if (tty_port_cts_enabled(&u->state->port))
Huang Shijie00592022012-08-08 10:37:59 +0800295 ctrl |= AUART_CTRL2_RTSEN;
296 }
297
Sascha Hauer47d37d62011-01-11 15:54:54 +0100298 s->ctrl = mctrl;
299 writel(ctrl, u->membase + AUART_CTRL2);
300}
301
302static u32 mxs_auart_get_mctrl(struct uart_port *u)
303{
304 struct mxs_auart_port *s = to_auart_port(u);
305 u32 stat = readl(u->membase + AUART_STAT);
306 int ctrl2 = readl(u->membase + AUART_CTRL2);
307 u32 mctrl = s->ctrl;
308
309 mctrl &= ~TIOCM_CTS;
310 if (stat & AUART_STAT_CTS)
311 mctrl |= TIOCM_CTS;
312
313 if (ctrl2 & AUART_CTRL2_RTS)
314 mctrl |= TIOCM_RTS;
315
316 return mctrl;
317}
318
319static void mxs_auart_settermios(struct uart_port *u,
320 struct ktermios *termios,
321 struct ktermios *old)
322{
323 u32 bm, ctrl, ctrl2, div;
324 unsigned int cflag, baud;
325
326 cflag = termios->c_cflag;
327
328 ctrl = AUART_LINECTRL_FEN;
329 ctrl2 = readl(u->membase + AUART_CTRL2);
330
331 /* byte size */
332 switch (cflag & CSIZE) {
333 case CS5:
334 bm = 0;
335 break;
336 case CS6:
337 bm = 1;
338 break;
339 case CS7:
340 bm = 2;
341 break;
342 case CS8:
343 bm = 3;
344 break;
345 default:
346 return;
347 }
348
349 ctrl |= AUART_LINECTRL_WLEN(bm);
350
351 /* parity */
352 if (cflag & PARENB) {
353 ctrl |= AUART_LINECTRL_PEN;
354 if ((cflag & PARODD) == 0)
355 ctrl |= AUART_LINECTRL_EPS;
356 }
357
358 u->read_status_mask = 0;
359
360 if (termios->c_iflag & INPCK)
361 u->read_status_mask |= AUART_STAT_PERR;
362 if (termios->c_iflag & (BRKINT | PARMRK))
363 u->read_status_mask |= AUART_STAT_BERR;
364
365 /*
366 * Characters to ignore
367 */
368 u->ignore_status_mask = 0;
369 if (termios->c_iflag & IGNPAR)
370 u->ignore_status_mask |= AUART_STAT_PERR;
371 if (termios->c_iflag & IGNBRK) {
372 u->ignore_status_mask |= AUART_STAT_BERR;
373 /*
374 * If we're ignoring parity and break indicators,
375 * ignore overruns too (for real raw support).
376 */
377 if (termios->c_iflag & IGNPAR)
378 u->ignore_status_mask |= AUART_STAT_OERR;
379 }
380
381 /*
382 * ignore all characters if CREAD is not set
383 */
384 if (cflag & CREAD)
385 ctrl2 |= AUART_CTRL2_RXE;
386 else
387 ctrl2 &= ~AUART_CTRL2_RXE;
388
389 /* figure out the stop bits requested */
390 if (cflag & CSTOPB)
391 ctrl |= AUART_LINECTRL_STP2;
392
393 /* figure out the hardware flow control settings */
394 if (cflag & CRTSCTS)
Huang Shijie00592022012-08-08 10:37:59 +0800395 ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN;
Sascha Hauer47d37d62011-01-11 15:54:54 +0100396 else
Huang Shijie00592022012-08-08 10:37:59 +0800397 ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100398
399 /* set baud rate */
400 baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk);
401 div = u->uartclk * 32 / baud;
402 ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F);
403 ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6);
404
405 writel(ctrl, u->membase + AUART_LINECTRL);
406 writel(ctrl2, u->membase + AUART_CTRL2);
Lothar Waßmann8b979f72012-05-03 11:37:12 +0200407
408 uart_update_timeout(u, termios->c_cflag, baud);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100409}
410
411static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
412{
413 u32 istatus, istat;
414 struct mxs_auart_port *s = context;
415 u32 stat = readl(s->port.membase + AUART_STAT);
416
417 istatus = istat = readl(s->port.membase + AUART_INTR);
418
419 if (istat & AUART_INTR_CTSMIS) {
420 uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS);
421 writel(AUART_INTR_CTSMIS,
422 s->port.membase + AUART_INTR_CLR);
423 istat &= ~AUART_INTR_CTSMIS;
424 }
425
426 if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) {
427 mxs_auart_rx_chars(s);
428 istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS);
429 }
430
431 if (istat & AUART_INTR_TXIS) {
432 mxs_auart_tx_chars(s);
433 istat &= ~AUART_INTR_TXIS;
434 }
435
436 writel(istatus & (AUART_INTR_RTIS
437 | AUART_INTR_TXIS
438 | AUART_INTR_RXIS
439 | AUART_INTR_CTSMIS),
440 s->port.membase + AUART_INTR_CLR);
441
442 return IRQ_HANDLED;
443}
444
445static void mxs_auart_reset(struct uart_port *u)
446{
447 int i;
448 unsigned int reg;
449
450 writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_CLR);
451
452 for (i = 0; i < 10000; i++) {
453 reg = readl(u->membase + AUART_CTRL0);
454 if (!(reg & AUART_CTRL0_SFTRST))
455 break;
456 udelay(3);
457 }
458 writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR);
459}
460
461static int mxs_auart_startup(struct uart_port *u)
462{
463 struct mxs_auart_port *s = to_auart_port(u);
464
Shawn Guoa4813772011-12-20 14:10:29 +0800465 clk_prepare_enable(s->clk);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100466
467 writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR);
468
469 writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_SET);
470
471 writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
472 u->membase + AUART_INTR);
473
474 /*
475 * Enable fifo so all four bytes of a DMA word are written to
476 * output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
477 */
478 writel(AUART_LINECTRL_FEN, u->membase + AUART_LINECTRL_SET);
479
480 return 0;
481}
482
483static void mxs_auart_shutdown(struct uart_port *u)
484{
485 struct mxs_auart_port *s = to_auart_port(u);
486
487 writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR);
488
Sascha Hauer47d37d62011-01-11 15:54:54 +0100489 writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
490 u->membase + AUART_INTR_CLR);
491
Huang Shijie851b7142012-09-06 22:38:40 -0400492 writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET);
493
Shawn Guoa4813772011-12-20 14:10:29 +0800494 clk_disable_unprepare(s->clk);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100495}
496
497static unsigned int mxs_auart_tx_empty(struct uart_port *u)
498{
499 if (readl(u->membase + AUART_STAT) & AUART_STAT_TXFE)
500 return TIOCSER_TEMT;
501 else
502 return 0;
503}
504
505static void mxs_auart_start_tx(struct uart_port *u)
506{
507 struct mxs_auart_port *s = to_auart_port(u);
508
509 /* enable transmitter */
510 writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_SET);
511
512 mxs_auart_tx_chars(s);
513}
514
515static void mxs_auart_stop_tx(struct uart_port *u)
516{
517 writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_CLR);
518}
519
520static void mxs_auart_stop_rx(struct uart_port *u)
521{
522 writel(AUART_CTRL2_RXE, u->membase + AUART_CTRL2_CLR);
523}
524
525static void mxs_auart_break_ctl(struct uart_port *u, int ctl)
526{
527 if (ctl)
528 writel(AUART_LINECTRL_BRK,
529 u->membase + AUART_LINECTRL_SET);
530 else
531 writel(AUART_LINECTRL_BRK,
532 u->membase + AUART_LINECTRL_CLR);
533}
534
535static void mxs_auart_enable_ms(struct uart_port *port)
536{
537 /* just empty */
538}
539
540static struct uart_ops mxs_auart_ops = {
541 .tx_empty = mxs_auart_tx_empty,
542 .start_tx = mxs_auart_start_tx,
543 .stop_tx = mxs_auart_stop_tx,
544 .stop_rx = mxs_auart_stop_rx,
545 .enable_ms = mxs_auart_enable_ms,
546 .break_ctl = mxs_auart_break_ctl,
547 .set_mctrl = mxs_auart_set_mctrl,
548 .get_mctrl = mxs_auart_get_mctrl,
549 .startup = mxs_auart_startup,
550 .shutdown = mxs_auart_shutdown,
551 .set_termios = mxs_auart_settermios,
552 .type = mxs_auart_type,
553 .release_port = mxs_auart_release_port,
554 .request_port = mxs_auart_request_port,
555 .config_port = mxs_auart_config_port,
556 .verify_port = mxs_auart_verify_port,
557};
558
559static struct mxs_auart_port *auart_port[MXS_AUART_PORTS];
560
561#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
562static void mxs_auart_console_putchar(struct uart_port *port, int ch)
563{
564 unsigned int to = 1000;
565
566 while (readl(port->membase + AUART_STAT) & AUART_STAT_TXFF) {
567 if (!to--)
568 break;
569 udelay(1);
570 }
571
572 writel(ch, port->membase + AUART_DATA);
573}
574
575static void
576auart_console_write(struct console *co, const char *str, unsigned int count)
577{
578 struct mxs_auart_port *s;
579 struct uart_port *port;
580 unsigned int old_ctrl0, old_ctrl2;
581 unsigned int to = 1000;
582
583 if (co->index > MXS_AUART_PORTS || co->index < 0)
584 return;
585
586 s = auart_port[co->index];
587 port = &s->port;
588
589 clk_enable(s->clk);
590
591 /* First save the CR then disable the interrupts */
592 old_ctrl2 = readl(port->membase + AUART_CTRL2);
593 old_ctrl0 = readl(port->membase + AUART_CTRL0);
594
595 writel(AUART_CTRL0_CLKGATE,
596 port->membase + AUART_CTRL0_CLR);
597 writel(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE,
598 port->membase + AUART_CTRL2_SET);
599
600 uart_console_write(port, str, count, mxs_auart_console_putchar);
601
602 /*
603 * Finally, wait for transmitter to become empty
604 * and restore the TCR
605 */
606 while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) {
607 if (!to--)
608 break;
609 udelay(1);
610 }
611
612 writel(old_ctrl0, port->membase + AUART_CTRL0);
613 writel(old_ctrl2, port->membase + AUART_CTRL2);
614
615 clk_disable(s->clk);
616}
617
618static void __init
619auart_console_get_options(struct uart_port *port, int *baud,
620 int *parity, int *bits)
621{
622 unsigned int lcr_h, quot;
623
624 if (!(readl(port->membase + AUART_CTRL2) & AUART_CTRL2_UARTEN))
625 return;
626
627 lcr_h = readl(port->membase + AUART_LINECTRL);
628
629 *parity = 'n';
630 if (lcr_h & AUART_LINECTRL_PEN) {
631 if (lcr_h & AUART_LINECTRL_EPS)
632 *parity = 'e';
633 else
634 *parity = 'o';
635 }
636
637 if ((lcr_h & AUART_LINECTRL_WLEN_MASK) == AUART_LINECTRL_WLEN(2))
638 *bits = 7;
639 else
640 *bits = 8;
641
642 quot = ((readl(port->membase + AUART_LINECTRL)
643 & AUART_LINECTRL_BAUD_DIVINT_MASK))
644 >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6);
645 quot |= ((readl(port->membase + AUART_LINECTRL)
646 & AUART_LINECTRL_BAUD_DIVFRAC_MASK))
647 >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT;
648 if (quot == 0)
649 quot = 1;
650
651 *baud = (port->uartclk << 2) / quot;
652}
653
654static int __init
655auart_console_setup(struct console *co, char *options)
656{
657 struct mxs_auart_port *s;
658 int baud = 9600;
659 int bits = 8;
660 int parity = 'n';
661 int flow = 'n';
662 int ret;
663
664 /*
665 * Check whether an invalid uart number has been specified, and
666 * if so, search for the first available port that does have
667 * console support.
668 */
669 if (co->index == -1 || co->index >= ARRAY_SIZE(auart_port))
670 co->index = 0;
671 s = auart_port[co->index];
672 if (!s)
673 return -ENODEV;
674
Shawn Guoa4813772011-12-20 14:10:29 +0800675 clk_prepare_enable(s->clk);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100676
677 if (options)
678 uart_parse_options(options, &baud, &parity, &bits, &flow);
679 else
680 auart_console_get_options(&s->port, &baud, &parity, &bits);
681
682 ret = uart_set_options(&s->port, co, baud, parity, bits, flow);
683
Shawn Guoa4813772011-12-20 14:10:29 +0800684 clk_disable_unprepare(s->clk);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100685
686 return ret;
687}
688
689static struct console auart_console = {
690 .name = "ttyAPP",
691 .write = auart_console_write,
692 .device = uart_console_device,
693 .setup = auart_console_setup,
694 .flags = CON_PRINTBUFFER,
695 .index = -1,
696 .data = &auart_driver,
697};
698#endif
699
700static struct uart_driver auart_driver = {
701 .owner = THIS_MODULE,
702 .driver_name = "ttyAPP",
703 .dev_name = "ttyAPP",
704 .major = 0,
705 .minor = 0,
706 .nr = MXS_AUART_PORTS,
707#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
708 .cons = &auart_console,
709#endif
710};
711
Fabio Estevam1ea66072012-06-18 10:06:09 -0300712/*
713 * This function returns 1 if pdev isn't a device instatiated by dt, 0 if it
714 * could successfully get all information from dt or a negative errno.
715 */
716static int serial_mxs_probe_dt(struct mxs_auart_port *s,
717 struct platform_device *pdev)
718{
719 struct device_node *np = pdev->dev.of_node;
720 int ret;
721
722 if (!np)
723 /* no device tree device */
724 return 1;
725
726 ret = of_alias_get_id(np, "serial");
727 if (ret < 0) {
728 dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
729 return ret;
730 }
731 s->port.line = ret;
732
733 return 0;
734}
735
Sascha Hauer47d37d62011-01-11 15:54:54 +0100736static int __devinit mxs_auart_probe(struct platform_device *pdev)
737{
Huang Shijief4b1f03b2012-11-16 16:03:52 +0800738 const struct of_device_id *of_id =
739 of_match_device(mxs_auart_dt_ids, &pdev->dev);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100740 struct mxs_auart_port *s;
741 u32 version;
742 int ret = 0;
743 struct resource *r;
Shawn Guo2e174c32012-05-06 22:54:26 +0800744 struct pinctrl *pinctrl;
Sascha Hauer47d37d62011-01-11 15:54:54 +0100745
746 s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
747 if (!s) {
748 ret = -ENOMEM;
749 goto out;
750 }
751
Fabio Estevam1ea66072012-06-18 10:06:09 -0300752 ret = serial_mxs_probe_dt(s, pdev);
753 if (ret > 0)
754 s->port.line = pdev->id < 0 ? 0 : pdev->id;
755 else if (ret < 0)
756 goto out_free;
757
Shawn Guo2e174c32012-05-06 22:54:26 +0800758 pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
759 if (IS_ERR(pinctrl)) {
760 ret = PTR_ERR(pinctrl);
761 goto out_free;
762 }
763
Huang Shijief4b1f03b2012-11-16 16:03:52 +0800764 if (of_id) {
765 pdev->id_entry = of_id->data;
766 s->devtype = pdev->id_entry->driver_data;
767 }
768
Sascha Hauer47d37d62011-01-11 15:54:54 +0100769 s->clk = clk_get(&pdev->dev, NULL);
770 if (IS_ERR(s->clk)) {
771 ret = PTR_ERR(s->clk);
772 goto out_free;
773 }
774
775 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
776 if (!r) {
777 ret = -ENXIO;
778 goto out_free_clk;
779 }
780
781 s->port.mapbase = r->start;
782 s->port.membase = ioremap(r->start, resource_size(r));
783 s->port.ops = &mxs_auart_ops;
784 s->port.iotype = UPIO_MEM;
Sascha Hauer47d37d62011-01-11 15:54:54 +0100785 s->port.fifosize = 16;
786 s->port.uartclk = clk_get_rate(s->clk);
787 s->port.type = PORT_IMX;
788 s->port.dev = s->dev = get_device(&pdev->dev);
789
790 s->flags = 0;
791 s->ctrl = 0;
792
793 s->irq = platform_get_irq(pdev, 0);
794 s->port.irq = s->irq;
795 ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s);
796 if (ret)
797 goto out_free_clk;
798
799 platform_set_drvdata(pdev, s);
800
Fabio Estevam1ea66072012-06-18 10:06:09 -0300801 auart_port[s->port.line] = s;
Sascha Hauer47d37d62011-01-11 15:54:54 +0100802
803 mxs_auart_reset(&s->port);
804
805 ret = uart_add_one_port(&auart_driver, &s->port);
806 if (ret)
807 goto out_free_irq;
808
809 version = readl(s->port.membase + AUART_VERSION);
810 dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n",
811 (version >> 24) & 0xff,
812 (version >> 16) & 0xff, version & 0xffff);
813
814 return 0;
815
816out_free_irq:
817 auart_port[pdev->id] = NULL;
818 free_irq(s->irq, s);
819out_free_clk:
Huang Shijie23666a72012-09-11 15:30:30 +0800820 put_device(s->dev);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100821 clk_put(s->clk);
822out_free:
823 kfree(s);
824out:
825 return ret;
826}
827
828static int __devexit mxs_auart_remove(struct platform_device *pdev)
829{
830 struct mxs_auart_port *s = platform_get_drvdata(pdev);
831
832 uart_remove_one_port(&auart_driver, &s->port);
833
834 auart_port[pdev->id] = NULL;
835
Huang Shijieb69200f2012-09-06 22:38:41 -0400836 put_device(s->dev);
Sascha Hauer47d37d62011-01-11 15:54:54 +0100837 clk_put(s->clk);
838 free_irq(s->irq, s);
839 kfree(s);
840
841 return 0;
842}
843
844static struct platform_driver mxs_auart_driver = {
845 .probe = mxs_auart_probe,
846 .remove = __devexit_p(mxs_auart_remove),
847 .driver = {
848 .name = "mxs-auart",
849 .owner = THIS_MODULE,
Fabio Estevam1ea66072012-06-18 10:06:09 -0300850 .of_match_table = mxs_auart_dt_ids,
Sascha Hauer47d37d62011-01-11 15:54:54 +0100851 },
852};
853
854static int __init mxs_auart_init(void)
855{
856 int r;
857
858 r = uart_register_driver(&auart_driver);
859 if (r)
860 goto out;
861
862 r = platform_driver_register(&mxs_auart_driver);
863 if (r)
864 goto out_err;
865
866 return 0;
867out_err:
868 uart_unregister_driver(&auart_driver);
869out:
870 return r;
871}
872
873static void __exit mxs_auart_exit(void)
874{
875 platform_driver_unregister(&mxs_auart_driver);
876 uart_unregister_driver(&auart_driver);
877}
878
879module_init(mxs_auart_init);
880module_exit(mxs_auart_exit);
881MODULE_LICENSE("GPL");
882MODULE_DESCRIPTION("Freescale MXS application uart driver");
Fabio Estevam1ea66072012-06-18 10:06:09 -0300883MODULE_ALIAS("platform:mxs-auart");