blob: 127d6cd5de7ffb636b0b75bf613f6702d2d3aa18 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/char/amba.c
3 *
4 * Driver for AMBA serial ports
5 *
6 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
7 *
8 * Copyright 1999 ARM Limited
9 * Copyright (C) 2000 Deep Blue Solutions Ltd.
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 * $Id: amba.c,v 1.41 2002/07/28 10:03:27 rmk Exp $
26 *
27 * This is a generic driver for ARM AMBA-type serial ports. They
28 * have a lot of 16550-like features, but are not register compatible.
29 * Note that although they do have CTS, DCD and DSR inputs, they do
30 * not have an RI input, nor do they have DTR or RTS outputs. If
31 * required, these have to be supplied via some other means (eg, GPIO)
32 * and hooked into this driver.
33 */
34#include <linux/config.h>
35
36#if defined(CONFIG_SERIAL_AMBA_PL010_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
37#define SUPPORT_SYSRQ
38#endif
39
40#include <linux/module.h>
41#include <linux/ioport.h>
42#include <linux/init.h>
43#include <linux/console.h>
44#include <linux/sysrq.h>
45#include <linux/device.h>
46#include <linux/tty.h>
47#include <linux/tty_flip.h>
48#include <linux/serial_core.h>
49#include <linux/serial.h>
Russell Kinga62c80e2006-01-07 13:52:45 +000050#include <linux/amba/bus.h>
51#include <linux/amba/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53#include <asm/io.h>
54#include <asm/irq.h>
Russell Kingc6b8fda2005-10-28 14:05:16 +010055#include <asm/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57#define UART_NR 2
58
59#define SERIAL_AMBA_MAJOR 204
60#define SERIAL_AMBA_MINOR 16
61#define SERIAL_AMBA_NR UART_NR
62
63#define AMBA_ISR_PASS_LIMIT 256
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0)
66#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68#define UART_DUMMY_RSR_RX /*256*/0
69#define UART_PORT_SIZE 64
70
71/*
72 * On the Integrator platform, the port RTS and DTR are provided by
73 * bits in the following SC_CTRLS register bits:
74 * RTS DTR
75 * UART0 7 6
76 * UART1 5 4
77 */
78#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
79#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
80
81/*
82 * We wrap our port structure around the generic uart_port.
83 */
84struct uart_amba_port {
85 struct uart_port port;
86 unsigned int dtr_mask;
87 unsigned int rts_mask;
88 unsigned int old_status;
89};
90
Russell Kingb129a8c2005-08-31 10:12:14 +010091static void pl010_stop_tx(struct uart_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
93 unsigned int cr;
94
Russell King98639a62006-03-25 21:30:11 +000095 cr = readb(port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 cr &= ~UART010_CR_TIE;
Russell King98639a62006-03-25 21:30:11 +000097 writel(cr, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
99
Russell Kingb129a8c2005-08-31 10:12:14 +0100100static void pl010_start_tx(struct uart_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101{
102 unsigned int cr;
103
Russell King98639a62006-03-25 21:30:11 +0000104 cr = readb(port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 cr |= UART010_CR_TIE;
Russell King98639a62006-03-25 21:30:11 +0000106 writel(cr, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
109static void pl010_stop_rx(struct uart_port *port)
110{
111 unsigned int cr;
112
Russell King98639a62006-03-25 21:30:11 +0000113 cr = readb(port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 cr &= ~(UART010_CR_RIE | UART010_CR_RTIE);
Russell King98639a62006-03-25 21:30:11 +0000115 writel(cr, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
117
118static void pl010_enable_ms(struct uart_port *port)
119{
120 unsigned int cr;
121
Russell King98639a62006-03-25 21:30:11 +0000122 cr = readb(port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 cr |= UART010_CR_MSIE;
Russell King98639a62006-03-25 21:30:11 +0000124 writel(cr, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125}
126
127static void
128#ifdef SUPPORT_SYSRQ
129pl010_rx_chars(struct uart_port *port, struct pt_regs *regs)
130#else
131pl010_rx_chars(struct uart_port *port)
132#endif
133{
134 struct tty_struct *tty = port->info->tty;
135 unsigned int status, ch, flag, rsr, max_count = 256;
136
Russell King98639a62006-03-25 21:30:11 +0000137 status = readb(port->membase + UART01x_FR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 while (UART_RX_DATA(status) && max_count--) {
Russell King98639a62006-03-25 21:30:11 +0000139 ch = readb(port->membase + UART01x_DR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 flag = TTY_NORMAL;
141
142 port->icount.rx++;
143
144 /*
145 * Note that the error handling code is
146 * out of the main execution path
147 */
Russell King98639a62006-03-25 21:30:11 +0000148 rsr = readb(port->membase + UART01x_RSR) | UART_DUMMY_RSR_RX;
Russell King45849282005-04-26 15:29:44 +0100149 if (unlikely(rsr & UART01x_RSR_ANY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 if (rsr & UART01x_RSR_BE) {
151 rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);
152 port->icount.brk++;
153 if (uart_handle_break(port))
154 goto ignore_char;
155 } else if (rsr & UART01x_RSR_PE)
156 port->icount.parity++;
157 else if (rsr & UART01x_RSR_FE)
158 port->icount.frame++;
159 if (rsr & UART01x_RSR_OE)
160 port->icount.overrun++;
161
162 rsr &= port->read_status_mask;
163
164 if (rsr & UART01x_RSR_BE)
165 flag = TTY_BREAK;
166 else if (rsr & UART01x_RSR_PE)
167 flag = TTY_PARITY;
168 else if (rsr & UART01x_RSR_FE)
169 flag = TTY_FRAME;
170 }
171
172 if (uart_handle_sysrq_char(port, ch, regs))
173 goto ignore_char;
174
Russell King05ab3012005-05-09 23:21:59 +0100175 uart_insert_char(port, rsr, UART01x_RSR_OE, ch, flag);
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 ignore_char:
Russell King98639a62006-03-25 21:30:11 +0000178 status = readb(port->membase + UART01x_FR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 }
180 tty_flip_buffer_push(tty);
181 return;
182}
183
184static void pl010_tx_chars(struct uart_port *port)
185{
186 struct circ_buf *xmit = &port->info->xmit;
187 int count;
188
189 if (port->x_char) {
Russell King98639a62006-03-25 21:30:11 +0000190 writel(port->x_char, port->membase + UART01x_DR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 port->icount.tx++;
192 port->x_char = 0;
193 return;
194 }
195 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
Russell Kingb129a8c2005-08-31 10:12:14 +0100196 pl010_stop_tx(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 return;
198 }
199
200 count = port->fifosize >> 1;
201 do {
Russell King98639a62006-03-25 21:30:11 +0000202 writel(xmit->buf[xmit->tail], port->membase + UART01x_DR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
204 port->icount.tx++;
205 if (uart_circ_empty(xmit))
206 break;
207 } while (--count > 0);
208
209 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
210 uart_write_wakeup(port);
211
212 if (uart_circ_empty(xmit))
Russell Kingb129a8c2005-08-31 10:12:14 +0100213 pl010_stop_tx(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214}
215
216static void pl010_modem_status(struct uart_port *port)
217{
218 struct uart_amba_port *uap = (struct uart_amba_port *)port;
219 unsigned int status, delta;
220
Russell King98639a62006-03-25 21:30:11 +0000221 writel(0, uap->port.membase + UART010_ICR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
Russell King98639a62006-03-25 21:30:11 +0000223 status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
225 delta = status ^ uap->old_status;
226 uap->old_status = status;
227
228 if (!delta)
229 return;
230
231 if (delta & UART01x_FR_DCD)
232 uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
233
234 if (delta & UART01x_FR_DSR)
235 uap->port.icount.dsr++;
236
237 if (delta & UART01x_FR_CTS)
238 uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
239
240 wake_up_interruptible(&uap->port.info->delta_msr_wait);
241}
242
243static irqreturn_t pl010_int(int irq, void *dev_id, struct pt_regs *regs)
244{
245 struct uart_port *port = dev_id;
246 unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
247 int handled = 0;
248
249 spin_lock(&port->lock);
250
Russell King98639a62006-03-25 21:30:11 +0000251 status = readb(port->membase + UART010_IIR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 if (status) {
253 do {
254 if (status & (UART010_IIR_RTIS | UART010_IIR_RIS))
255#ifdef SUPPORT_SYSRQ
256 pl010_rx_chars(port, regs);
257#else
258 pl010_rx_chars(port);
259#endif
260 if (status & UART010_IIR_MIS)
261 pl010_modem_status(port);
262 if (status & UART010_IIR_TIS)
263 pl010_tx_chars(port);
264
265 if (pass_counter-- == 0)
266 break;
267
Russell King98639a62006-03-25 21:30:11 +0000268 status = readb(port->membase + UART010_IIR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 } while (status & (UART010_IIR_RTIS | UART010_IIR_RIS |
270 UART010_IIR_TIS));
271 handled = 1;
272 }
273
274 spin_unlock(&port->lock);
275
276 return IRQ_RETVAL(handled);
277}
278
279static unsigned int pl010_tx_empty(struct uart_port *port)
280{
Russell King98639a62006-03-25 21:30:11 +0000281 return readb(port->membase + UART01x_FR) & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282}
283
284static unsigned int pl010_get_mctrl(struct uart_port *port)
285{
286 unsigned int result = 0;
287 unsigned int status;
288
Russell King98639a62006-03-25 21:30:11 +0000289 status = readb(port->membase + UART01x_FR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 if (status & UART01x_FR_DCD)
291 result |= TIOCM_CAR;
292 if (status & UART01x_FR_DSR)
293 result |= TIOCM_DSR;
294 if (status & UART01x_FR_CTS)
295 result |= TIOCM_CTS;
296
297 return result;
298}
299
300static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl)
301{
302 struct uart_amba_port *uap = (struct uart_amba_port *)port;
303 unsigned int ctrls = 0, ctrlc = 0;
304
305 if (mctrl & TIOCM_RTS)
306 ctrlc |= uap->rts_mask;
307 else
308 ctrls |= uap->rts_mask;
309
310 if (mctrl & TIOCM_DTR)
311 ctrlc |= uap->dtr_mask;
312 else
313 ctrls |= uap->dtr_mask;
314
315 __raw_writel(ctrls, SC_CTRLS);
316 __raw_writel(ctrlc, SC_CTRLC);
317}
318
319static void pl010_break_ctl(struct uart_port *port, int break_state)
320{
321 unsigned long flags;
322 unsigned int lcr_h;
323
324 spin_lock_irqsave(&port->lock, flags);
Russell King98639a62006-03-25 21:30:11 +0000325 lcr_h = readb(port->membase + UART010_LCRH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 if (break_state == -1)
327 lcr_h |= UART01x_LCRH_BRK;
328 else
329 lcr_h &= ~UART01x_LCRH_BRK;
Russell King98639a62006-03-25 21:30:11 +0000330 writel(lcr_h, port->membase + UART010_LCRH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 spin_unlock_irqrestore(&port->lock, flags);
332}
333
334static int pl010_startup(struct uart_port *port)
335{
336 struct uart_amba_port *uap = (struct uart_amba_port *)port;
337 int retval;
338
339 /*
340 * Allocate the IRQ
341 */
342 retval = request_irq(port->irq, pl010_int, 0, "uart-pl010", port);
343 if (retval)
344 return retval;
345
346 /*
347 * initialise the old status of the modem signals
348 */
Russell King98639a62006-03-25 21:30:11 +0000349 uap->old_status = readb(port->membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 /*
352 * Finally, enable interrupts
353 */
Russell King98639a62006-03-25 21:30:11 +0000354 writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE,
355 port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
357 return 0;
358}
359
360static void pl010_shutdown(struct uart_port *port)
361{
362 /*
363 * Free the interrupt
364 */
365 free_irq(port->irq, port);
366
367 /*
368 * disable all interrupts, disable the port
369 */
Russell King98639a62006-03-25 21:30:11 +0000370 writel(0, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 /* disable break condition and fifos */
Russell King98639a62006-03-25 21:30:11 +0000373 writel(readb(port->membase + UART010_LCRH) &
374 ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN),
375 port->membase + UART010_LCRH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
378static void
379pl010_set_termios(struct uart_port *port, struct termios *termios,
380 struct termios *old)
381{
382 unsigned int lcr_h, old_cr;
383 unsigned long flags;
384 unsigned int baud, quot;
385
386 /*
387 * Ask the core to calculate the divisor for us.
388 */
389 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
390 quot = uart_get_divisor(port, baud);
391
392 switch (termios->c_cflag & CSIZE) {
393 case CS5:
394 lcr_h = UART01x_LCRH_WLEN_5;
395 break;
396 case CS6:
397 lcr_h = UART01x_LCRH_WLEN_6;
398 break;
399 case CS7:
400 lcr_h = UART01x_LCRH_WLEN_7;
401 break;
402 default: // CS8
403 lcr_h = UART01x_LCRH_WLEN_8;
404 break;
405 }
406 if (termios->c_cflag & CSTOPB)
407 lcr_h |= UART01x_LCRH_STP2;
408 if (termios->c_cflag & PARENB) {
409 lcr_h |= UART01x_LCRH_PEN;
410 if (!(termios->c_cflag & PARODD))
411 lcr_h |= UART01x_LCRH_EPS;
412 }
413 if (port->fifosize > 1)
414 lcr_h |= UART01x_LCRH_FEN;
415
416 spin_lock_irqsave(&port->lock, flags);
417
418 /*
419 * Update the per-port timeout.
420 */
421 uart_update_timeout(port, termios->c_cflag, baud);
422
423 port->read_status_mask = UART01x_RSR_OE;
424 if (termios->c_iflag & INPCK)
425 port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
426 if (termios->c_iflag & (BRKINT | PARMRK))
427 port->read_status_mask |= UART01x_RSR_BE;
428
429 /*
430 * Characters to ignore
431 */
432 port->ignore_status_mask = 0;
433 if (termios->c_iflag & IGNPAR)
434 port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
435 if (termios->c_iflag & IGNBRK) {
436 port->ignore_status_mask |= UART01x_RSR_BE;
437 /*
438 * If we're ignoring parity and break indicators,
439 * ignore overruns too (for real raw support).
440 */
441 if (termios->c_iflag & IGNPAR)
442 port->ignore_status_mask |= UART01x_RSR_OE;
443 }
444
445 /*
446 * Ignore all characters if CREAD is not set.
447 */
448 if ((termios->c_cflag & CREAD) == 0)
449 port->ignore_status_mask |= UART_DUMMY_RSR_RX;
450
451 /* first, disable everything */
Russell King98639a62006-03-25 21:30:11 +0000452 old_cr = readb(port->membase + UART010_CR) & ~UART010_CR_MSIE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
454 if (UART_ENABLE_MS(port, termios->c_cflag))
455 old_cr |= UART010_CR_MSIE;
456
Russell King98639a62006-03-25 21:30:11 +0000457 writel(0, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 /* Set baud rate */
460 quot -= 1;
Russell King98639a62006-03-25 21:30:11 +0000461 writel((quot & 0xf00) >> 8, port->membase + UART010_LCRM);
462 writel(quot & 0xff, port->membase + UART010_LCRL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
464 /*
465 * ----------v----------v----------v----------v-----
466 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
467 * ----------^----------^----------^----------^-----
468 */
Russell King98639a62006-03-25 21:30:11 +0000469 writel(lcr_h, port->membase + UART010_LCRH);
470 writel(old_cr, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
472 spin_unlock_irqrestore(&port->lock, flags);
473}
474
475static const char *pl010_type(struct uart_port *port)
476{
477 return port->type == PORT_AMBA ? "AMBA" : NULL;
478}
479
480/*
481 * Release the memory region(s) being used by 'port'
482 */
483static void pl010_release_port(struct uart_port *port)
484{
485 release_mem_region(port->mapbase, UART_PORT_SIZE);
486}
487
488/*
489 * Request the memory region(s) being used by 'port'
490 */
491static int pl010_request_port(struct uart_port *port)
492{
493 return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010")
494 != NULL ? 0 : -EBUSY;
495}
496
497/*
498 * Configure/autoconfigure the port.
499 */
500static void pl010_config_port(struct uart_port *port, int flags)
501{
502 if (flags & UART_CONFIG_TYPE) {
503 port->type = PORT_AMBA;
504 pl010_request_port(port);
505 }
506}
507
508/*
509 * verify the new serial_struct (for TIOCSSERIAL).
510 */
511static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
512{
513 int ret = 0;
514 if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
515 ret = -EINVAL;
516 if (ser->irq < 0 || ser->irq >= NR_IRQS)
517 ret = -EINVAL;
518 if (ser->baud_base < 9600)
519 ret = -EINVAL;
520 return ret;
521}
522
523static struct uart_ops amba_pl010_pops = {
524 .tx_empty = pl010_tx_empty,
525 .set_mctrl = pl010_set_mctrl,
526 .get_mctrl = pl010_get_mctrl,
527 .stop_tx = pl010_stop_tx,
528 .start_tx = pl010_start_tx,
529 .stop_rx = pl010_stop_rx,
530 .enable_ms = pl010_enable_ms,
531 .break_ctl = pl010_break_ctl,
532 .startup = pl010_startup,
533 .shutdown = pl010_shutdown,
534 .set_termios = pl010_set_termios,
535 .type = pl010_type,
536 .release_port = pl010_release_port,
537 .request_port = pl010_request_port,
538 .config_port = pl010_config_port,
539 .verify_port = pl010_verify_port,
540};
541
542static struct uart_amba_port amba_ports[UART_NR] = {
543 {
544 .port = {
545 .membase = (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE),
546 .mapbase = INTEGRATOR_UART0_BASE,
Russell King9b4a1612006-02-05 10:48:10 +0000547 .iotype = UPIO_MEM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 .irq = IRQ_UARTINT0,
549 .uartclk = 14745600,
550 .fifosize = 16,
551 .ops = &amba_pl010_pops,
Russell Kingce8337c2006-01-21 19:28:15 +0000552 .flags = UPF_BOOT_AUTOCONF,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 .line = 0,
554 },
555 .dtr_mask = 1 << 5,
556 .rts_mask = 1 << 4,
557 },
558 {
559 .port = {
560 .membase = (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE),
561 .mapbase = INTEGRATOR_UART1_BASE,
Russell King9b4a1612006-02-05 10:48:10 +0000562 .iotype = UPIO_MEM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 .irq = IRQ_UARTINT1,
564 .uartclk = 14745600,
565 .fifosize = 16,
566 .ops = &amba_pl010_pops,
Russell Kingce8337c2006-01-21 19:28:15 +0000567 .flags = UPF_BOOT_AUTOCONF,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 .line = 1,
569 },
570 .dtr_mask = 1 << 7,
571 .rts_mask = 1 << 6,
572 }
573};
574
575#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE
576
Russell Kingd3587882006-03-20 20:00:09 +0000577static void pl010_console_putchar(struct uart_port *port, int ch)
578{
Russell King98639a62006-03-25 21:30:11 +0000579 unsigned int status;
580
581 do {
582 status = readb(port->membase + UART01x_FR);
Russell Kingd3587882006-03-20 20:00:09 +0000583 barrier();
Russell King98639a62006-03-25 21:30:11 +0000584 } while (!UART_TX_READY(status));
585 writel(ch, port->membase + UART01x_DR);
Russell Kingd3587882006-03-20 20:00:09 +0000586}
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588static void
589pl010_console_write(struct console *co, const char *s, unsigned int count)
590{
591 struct uart_port *port = &amba_ports[co->index].port;
592 unsigned int status, old_cr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
594 /*
595 * First save the CR then disable the interrupts
596 */
Russell King98639a62006-03-25 21:30:11 +0000597 old_cr = readb(port->membase + UART010_CR);
598 writel(UART01x_CR_UARTEN, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Russell Kingd3587882006-03-20 20:00:09 +0000600 uart_console_write(port, s, count, pl010_console_putchar);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 /*
603 * Finally, wait for transmitter to become empty
604 * and restore the TCR
605 */
606 do {
Russell King98639a62006-03-25 21:30:11 +0000607 status = readb(port->membase + UART01x_FR);
608 barrier();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 } while (status & UART01x_FR_BUSY);
Russell King98639a62006-03-25 21:30:11 +0000610 writel(old_cr, port->membase + UART010_CR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
613static void __init
614pl010_console_get_options(struct uart_port *port, int *baud,
615 int *parity, int *bits)
616{
Russell King98639a62006-03-25 21:30:11 +0000617 if (readb(port->membase + UART010_CR) & UART01x_CR_UARTEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 unsigned int lcr_h, quot;
Russell King98639a62006-03-25 21:30:11 +0000619 lcr_h = readb(port->membase + UART010_LCRH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
621 *parity = 'n';
622 if (lcr_h & UART01x_LCRH_PEN) {
623 if (lcr_h & UART01x_LCRH_EPS)
624 *parity = 'e';
625 else
626 *parity = 'o';
627 }
628
629 if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
630 *bits = 7;
631 else
632 *bits = 8;
633
Russell King98639a62006-03-25 21:30:11 +0000634 quot = readb(port->membase + UART010_LCRL) | readb(port->membase + UART010_LCRM) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 *baud = port->uartclk / (16 * (quot + 1));
636 }
637}
638
639static int __init pl010_console_setup(struct console *co, char *options)
640{
641 struct uart_port *port;
642 int baud = 38400;
643 int bits = 8;
644 int parity = 'n';
645 int flow = 'n';
646
647 /*
648 * Check whether an invalid uart number has been specified, and
649 * if so, search for the first available port that does have
650 * console support.
651 */
652 if (co->index >= UART_NR)
653 co->index = 0;
654 port = &amba_ports[co->index].port;
655
656 if (options)
657 uart_parse_options(options, &baud, &parity, &bits, &flow);
658 else
659 pl010_console_get_options(port, &baud, &parity, &bits);
660
661 return uart_set_options(port, co, baud, parity, bits, flow);
662}
663
Vincent Sanders2d934862005-09-14 22:36:03 +0100664static struct uart_driver amba_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665static struct console amba_console = {
666 .name = "ttyAM",
667 .write = pl010_console_write,
668 .device = uart_console_device,
669 .setup = pl010_console_setup,
670 .flags = CON_PRINTBUFFER,
671 .index = -1,
672 .data = &amba_reg,
673};
674
675static int __init amba_console_init(void)
676{
677 /*
678 * All port initializations are done statically
679 */
680 register_console(&amba_console);
681 return 0;
682}
683console_initcall(amba_console_init);
684
685static int __init amba_late_console_init(void)
686{
687 if (!(amba_console.flags & CON_ENABLED))
688 register_console(&amba_console);
689 return 0;
690}
691late_initcall(amba_late_console_init);
692
693#define AMBA_CONSOLE &amba_console
694#else
695#define AMBA_CONSOLE NULL
696#endif
697
698static struct uart_driver amba_reg = {
699 .owner = THIS_MODULE,
700 .driver_name = "ttyAM",
701 .dev_name = "ttyAM",
702 .major = SERIAL_AMBA_MAJOR,
703 .minor = SERIAL_AMBA_MINOR,
704 .nr = UART_NR,
705 .cons = AMBA_CONSOLE,
706};
707
708static int pl010_probe(struct amba_device *dev, void *id)
709{
710 int i;
711
712 for (i = 0; i < UART_NR; i++) {
713 if (amba_ports[i].port.mapbase != dev->res.start)
714 continue;
715
716 amba_ports[i].port.dev = &dev->dev;
717 uart_add_one_port(&amba_reg, &amba_ports[i].port);
718 amba_set_drvdata(dev, &amba_ports[i]);
719 break;
720 }
721
722 return 0;
723}
724
725static int pl010_remove(struct amba_device *dev)
726{
727 struct uart_amba_port *uap = amba_get_drvdata(dev);
728
729 if (uap)
730 uart_remove_one_port(&amba_reg, &uap->port);
731
732 amba_set_drvdata(dev, NULL);
733
734 return 0;
735}
736
Pavel Machek0370aff2005-04-16 15:25:35 -0700737static int pl010_suspend(struct amba_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738{
739 struct uart_amba_port *uap = amba_get_drvdata(dev);
740
741 if (uap)
742 uart_suspend_port(&amba_reg, &uap->port);
743
744 return 0;
745}
746
747static int pl010_resume(struct amba_device *dev)
748{
749 struct uart_amba_port *uap = amba_get_drvdata(dev);
750
751 if (uap)
752 uart_resume_port(&amba_reg, &uap->port);
753
754 return 0;
755}
756
757static struct amba_id pl010_ids[] __initdata = {
758 {
759 .id = 0x00041010,
760 .mask = 0x000fffff,
761 },
762 { 0, 0 },
763};
764
765static struct amba_driver pl010_driver = {
766 .drv = {
767 .name = "uart-pl010",
768 },
769 .id_table = pl010_ids,
770 .probe = pl010_probe,
771 .remove = pl010_remove,
772 .suspend = pl010_suspend,
773 .resume = pl010_resume,
774};
775
776static int __init pl010_init(void)
777{
778 int ret;
779
780 printk(KERN_INFO "Serial: AMBA driver $Revision: 1.41 $\n");
781
782 ret = uart_register_driver(&amba_reg);
783 if (ret == 0) {
784 ret = amba_driver_register(&pl010_driver);
785 if (ret)
786 uart_unregister_driver(&amba_reg);
787 }
788 return ret;
789}
790
791static void __exit pl010_exit(void)
792{
793 amba_driver_unregister(&pl010_driver);
794 uart_unregister_driver(&amba_reg);
795}
796
797module_init(pl010_init);
798module_exit(pl010_exit);
799
800MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
801MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.41 $");
802MODULE_LICENSE("GPL");