blob: 85cfa08d3bad7955bb79279f73be920e332fb03f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* drivers/serial/serial_lh7a40x.c
2 *
3 * Copyright (C) 2004 Coastal Environmental Systems
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
8 *
9 */
10
11/* Driver for Sharp LH7A40X embedded serial ports
12 *
13 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
14 * Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
15 *
16 * ---
17 *
18 * This driver supports the embedded UARTs of the Sharp LH7A40X series
19 * CPUs. While similar to the 16550 and other UART chips, there is
20 * nothing close to register compatibility. Moreover, some of the
21 * modem control lines are not available, either in the chip or they
22 * are lacking in the board-level implementation.
23 *
24 * - Use of SIRDIS
25 * For simplicity, we disable the IR functions of any UART whenever
26 * we enable it.
27 *
28 */
29
30#include <linux/config.h>
31
32#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
33#define SUPPORT_SYSRQ
34#endif
35
36#include <linux/module.h>
37#include <linux/ioport.h>
38#include <linux/init.h>
39#include <linux/console.h>
40#include <linux/sysrq.h>
41#include <linux/tty.h>
42#include <linux/tty_flip.h>
43#include <linux/serial_core.h>
44#include <linux/serial.h>
45
46#include <asm/io.h>
47#include <asm/irq.h>
48
49#define DEV_MAJOR 204
50#define DEV_MINOR 16
51#define DEV_NR 3
52
53#define ISR_LOOP_LIMIT 256
54
55#define UR(p,o) _UR ((p)->membase, o)
56#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
57#define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m)
58#define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m)
59
60#define UART_REG_SIZE 32
61
62#define UART_R_DATA (0x00)
63#define UART_R_FCON (0x04)
64#define UART_R_BRCON (0x08)
65#define UART_R_CON (0x0c)
66#define UART_R_STATUS (0x10)
67#define UART_R_RAWISR (0x14)
68#define UART_R_INTEN (0x18)
69#define UART_R_ISR (0x1c)
70
71#define UARTEN (0x01) /* UART enable */
72#define SIRDIS (0x02) /* Serial IR disable (UART1 only) */
73
74#define RxEmpty (0x10)
75#define TxEmpty (0x80)
76#define TxFull (0x20)
77#define nRxRdy RxEmpty
78#define nTxRdy TxFull
79#define TxBusy (0x08)
80
81#define RxBreak (0x0800)
82#define RxOverrunError (0x0400)
83#define RxParityError (0x0200)
84#define RxFramingError (0x0100)
85#define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError)
86
87#define DCD (0x04)
88#define DSR (0x02)
89#define CTS (0x01)
90
91#define RxInt (0x01)
92#define TxInt (0x02)
93#define ModemInt (0x04)
94#define RxTimeoutInt (0x08)
95
96#define MSEOI (0x10)
97
98#define WLEN_8 (0x60)
99#define WLEN_7 (0x40)
100#define WLEN_6 (0x20)
101#define WLEN_5 (0x00)
102#define WLEN (0x60) /* Mask for all word-length bits */
103#define STP2 (0x08)
104#define PEN (0x02) /* Parity Enable */
105#define EPS (0x04) /* Even Parity Set */
106#define FEN (0x10) /* FIFO Enable */
107#define BRK (0x01) /* Send Break */
108
109
110struct uart_port_lh7a40x {
111 struct uart_port port;
112 unsigned int statusPrev; /* Most recently read modem status */
113};
114
115static void lh7a40xuart_stop_tx (struct uart_port* port, unsigned int tty_stop)
116{
117 BIT_CLR (port, UART_R_INTEN, TxInt);
118}
119
120static void lh7a40xuart_start_tx (struct uart_port* port,
121 unsigned int tty_start)
122{
123 BIT_SET (port, UART_R_INTEN, TxInt);
124
125 /* *** FIXME: do I need to check for startup of the
126 transmitter? The old driver did, but AMBA
127 doesn't . */
128}
129
130static void lh7a40xuart_stop_rx (struct uart_port* port)
131{
132 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
133}
134
135static void lh7a40xuart_enable_ms (struct uart_port* port)
136{
137 BIT_SET (port, UART_R_INTEN, ModemInt);
138}
139
140static void
141#ifdef SUPPORT_SYSRQ
142lh7a40xuart_rx_chars (struct uart_port* port, struct pt_regs* regs)
143#else
144lh7a40xuart_rx_chars (struct uart_port* port)
145#endif
146{
147 struct tty_struct* tty = port->info->tty;
148 int cbRxMax = 256; /* (Gross) limit on receive */
149 unsigned int data, flag;/* Received data and status */
150
151 while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
152 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
153 if (tty->low_latency)
154 tty_flip_buffer_push(tty);
155 /*
156 * If this failed then we will throw away the
157 * bytes but must do so to clear interrupts
158 */
159 }
160
161 data = UR (port, UART_R_DATA);
162 flag = TTY_NORMAL;
163 ++port->icount.rx;
164
Russell King45849282005-04-26 15:29:44 +0100165 if (unlikely(data & RxError)) { /* Quick check, short-circuit */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (data & RxBreak) {
167 data &= ~(RxFramingError | RxParityError);
168 ++port->icount.brk;
169 if (uart_handle_break (port))
170 continue;
171 }
172 else if (data & RxParityError)
173 ++port->icount.parity;
174 else if (data & RxFramingError)
175 ++port->icount.frame;
176 if (data & RxOverrunError)
177 ++port->icount.overrun;
178
179 /* Mask by termios, leave Rx'd byte */
180 data &= port->read_status_mask | 0xff;
181
182 if (data & RxBreak)
183 flag = TTY_BREAK;
184 else if (data & RxParityError)
185 flag = TTY_PARITY;
186 else if (data & RxFramingError)
187 flag = TTY_FRAME;
188 }
189
190 if (uart_handle_sysrq_char (port, (unsigned char) data, regs))
191 continue;
192
193 if ((data & port->ignore_status_mask) == 0) {
194 tty_insert_flip_char(tty, data, flag);
195 }
196 if ((data & RxOverrunError)
197 && tty->flip.count < TTY_FLIPBUF_SIZE) {
198 /*
199 * Overrun is special, since it's reported
200 * immediately, and doesn't affect the current
201 * character
202 */
203 tty_insert_flip_char(tty, 0, TTY_OVERRUN);
204 }
205 }
206 tty_flip_buffer_push (tty);
207 return;
208}
209
210static void lh7a40xuart_tx_chars (struct uart_port* port)
211{
212 struct circ_buf* xmit = &port->info->xmit;
213 int cbTxMax = port->fifosize;
214
215 if (port->x_char) {
216 UR (port, UART_R_DATA) = port->x_char;
217 ++port->icount.tx;
218 port->x_char = 0;
219 return;
220 }
221 if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
222 lh7a40xuart_stop_tx (port, 0);
223 return;
224 }
225
226 /* Unlike the AMBA UART, the lh7a40x UART does not guarantee
227 that at least half of the FIFO is empty. Instead, we check
228 status for every character. Using the AMBA method causes
229 the transmitter to drop characters. */
230
231 do {
232 UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
233 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
234 ++port->icount.tx;
235 if (uart_circ_empty(xmit))
236 break;
237 } while (!(UR (port, UART_R_STATUS) & nTxRdy)
238 && cbTxMax--);
239
240 if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
241 uart_write_wakeup (port);
242
243 if (uart_circ_empty (xmit))
244 lh7a40xuart_stop_tx (port, 0);
245}
246
247static void lh7a40xuart_modem_status (struct uart_port* port)
248{
249 unsigned int status = UR (port, UART_R_STATUS);
250 unsigned int delta
251 = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
252
253 BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
254
255 if (!delta) /* Only happens if we missed 2 transitions */
256 return;
257
258 ((struct uart_port_lh7a40x*) port)->statusPrev = status;
259
260 if (delta & DCD)
261 uart_handle_dcd_change (port, status & DCD);
262
263 if (delta & DSR)
264 ++port->icount.dsr;
265
266 if (delta & CTS)
267 uart_handle_cts_change (port, status & CTS);
268
269 wake_up_interruptible (&port->info->delta_msr_wait);
270}
271
272static irqreturn_t lh7a40xuart_int (int irq, void* dev_id,
273 struct pt_regs* regs)
274{
275 struct uart_port* port = dev_id;
276 unsigned int cLoopLimit = ISR_LOOP_LIMIT;
277 unsigned int isr = UR (port, UART_R_ISR);
278
279
280 do {
281 if (isr & (RxInt | RxTimeoutInt))
282#ifdef SUPPORT_SYSRQ
283 lh7a40xuart_rx_chars(port, regs);
284#else
285 lh7a40xuart_rx_chars(port);
286#endif
287 if (isr & ModemInt)
288 lh7a40xuart_modem_status (port);
289 if (isr & TxInt)
290 lh7a40xuart_tx_chars (port);
291
292 if (--cLoopLimit == 0)
293 break;
294
295 isr = UR (port, UART_R_ISR);
296 } while (isr & (RxInt | TxInt | RxTimeoutInt));
297
298 return IRQ_HANDLED;
299}
300
301static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
302{
303 return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
304}
305
306static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
307{
308 unsigned int result = 0;
309 unsigned int status = UR (port, UART_R_STATUS);
310
311 if (status & DCD)
312 result |= TIOCM_CAR;
313 if (status & DSR)
314 result |= TIOCM_DSR;
315 if (status & CTS)
316 result |= TIOCM_CTS;
317
318 return result;
319}
320
321static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
322{
323 /* None of the ports supports DTR. UART1 supports RTS through GPIO. */
324 /* Note, kernel appears to be setting DTR and RTS on console. */
325
326 /* *** FIXME: this deserves more work. There's some work in
327 tracing all of the IO pins. */
328#if 0
329 if( port->mapbase == UART1_PHYS) {
330 gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
331
332 if (mctrl & TIOCM_RTS)
333 gpio->pbdr &= ~GPIOB_UART1_RTS;
334 else
335 gpio->pbdr |= GPIOB_UART1_RTS;
336 }
337#endif
338}
339
340static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
341{
342 unsigned long flags;
343
344 spin_lock_irqsave(&port->lock, flags);
345 if (break_state == -1)
346 BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
347 else
348 BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
349 spin_unlock_irqrestore(&port->lock, flags);
350}
351
352static int lh7a40xuart_startup (struct uart_port* port)
353{
354 int retval;
355
356 retval = request_irq (port->irq, lh7a40xuart_int, 0,
357 "serial_lh7a40x", port);
358 if (retval)
359 return retval;
360
361 /* Initial modem control-line settings */
362 ((struct uart_port_lh7a40x*) port)->statusPrev
363 = UR (port, UART_R_STATUS);
364
365 /* There is presently no configuration option to enable IR.
366 Thus, we always disable it. */
367
368 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
369 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
370
371 return 0;
372}
373
374static void lh7a40xuart_shutdown (struct uart_port* port)
375{
376 free_irq (port->irq, port);
377 BIT_CLR (port, UART_R_FCON, BRK | FEN);
378 BIT_CLR (port, UART_R_CON, UARTEN);
379}
380
381static void lh7a40xuart_set_termios (struct uart_port* port,
382 struct termios* termios,
383 struct termios* old)
384{
385 unsigned int con;
386 unsigned int inten;
387 unsigned int fcon;
388 unsigned long flags;
389 unsigned int baud;
390 unsigned int quot;
391
392 baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
393 quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
394
395 switch (termios->c_cflag & CSIZE) {
396 case CS5:
397 fcon = WLEN_5;
398 break;
399 case CS6:
400 fcon = WLEN_6;
401 break;
402 case CS7:
403 fcon = WLEN_7;
404 break;
405 case CS8:
406 default:
407 fcon = WLEN_8;
408 break;
409 }
410 if (termios->c_cflag & CSTOPB)
411 fcon |= STP2;
412 if (termios->c_cflag & PARENB) {
413 fcon |= PEN;
414 if (!(termios->c_cflag & PARODD))
415 fcon |= EPS;
416 }
417 if (port->fifosize > 1)
418 fcon |= FEN;
419
420 spin_lock_irqsave (&port->lock, flags);
421
422 uart_update_timeout (port, termios->c_cflag, baud);
423
424 port->read_status_mask = RxOverrunError;
425 if (termios->c_iflag & INPCK)
426 port->read_status_mask |= RxFramingError | RxParityError;
427 if (termios->c_iflag & (BRKINT | PARMRK))
428 port->read_status_mask |= RxBreak;
429
430 /* Figure mask for status we ignore */
431 port->ignore_status_mask = 0;
432 if (termios->c_iflag & IGNPAR)
433 port->ignore_status_mask |= RxFramingError | RxParityError;
434 if (termios->c_iflag & IGNBRK) {
435 port->ignore_status_mask |= RxBreak;
436 /* Ignore overrun when ignorning parity */
437 /* *** FIXME: is this in the right place? */
438 if (termios->c_iflag & IGNPAR)
439 port->ignore_status_mask |= RxOverrunError;
440 }
441
442 /* Ignore all receive errors when receive disabled */
443 if ((termios->c_cflag & CREAD) == 0)
444 port->ignore_status_mask |= RxError;
445
446 con = UR (port, UART_R_CON);
447 inten = (UR (port, UART_R_INTEN) & ~ModemInt);
448
449 if (UART_ENABLE_MS (port, termios->c_cflag))
450 inten |= ModemInt;
451
452 BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
453 UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
454 UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
455 UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
456 UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
457 UR (port, UART_R_CON) = con; /* Restore UART mode */
458
459 spin_unlock_irqrestore(&port->lock, flags);
460}
461
462static const char* lh7a40xuart_type (struct uart_port* port)
463{
464 return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
465}
466
467static void lh7a40xuart_release_port (struct uart_port* port)
468{
469 release_mem_region (port->mapbase, UART_REG_SIZE);
470}
471
472static int lh7a40xuart_request_port (struct uart_port* port)
473{
474 return request_mem_region (port->mapbase, UART_REG_SIZE,
475 "serial_lh7a40x") != NULL
476 ? 0 : -EBUSY;
477}
478
479static void lh7a40xuart_config_port (struct uart_port* port, int flags)
480{
481 if (flags & UART_CONFIG_TYPE) {
482 port->type = PORT_LH7A40X;
483 lh7a40xuart_request_port (port);
484 }
485}
486
487static int lh7a40xuart_verify_port (struct uart_port* port,
488 struct serial_struct* ser)
489{
490 int ret = 0;
491
492 if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
493 ret = -EINVAL;
494 if (ser->irq < 0 || ser->irq >= NR_IRQS)
495 ret = -EINVAL;
496 if (ser->baud_base < 9600) /* *** FIXME: is this true? */
497 ret = -EINVAL;
498 return ret;
499}
500
501static struct uart_ops lh7a40x_uart_ops = {
502 .tx_empty = lh7a40xuart_tx_empty,
503 .set_mctrl = lh7a40xuart_set_mctrl,
504 .get_mctrl = lh7a40xuart_get_mctrl,
505 .stop_tx = lh7a40xuart_stop_tx,
506 .start_tx = lh7a40xuart_start_tx,
507 .stop_rx = lh7a40xuart_stop_rx,
508 .enable_ms = lh7a40xuart_enable_ms,
509 .break_ctl = lh7a40xuart_break_ctl,
510 .startup = lh7a40xuart_startup,
511 .shutdown = lh7a40xuart_shutdown,
512 .set_termios = lh7a40xuart_set_termios,
513 .type = lh7a40xuart_type,
514 .release_port = lh7a40xuart_release_port,
515 .request_port = lh7a40xuart_request_port,
516 .config_port = lh7a40xuart_config_port,
517 .verify_port = lh7a40xuart_verify_port,
518};
519
520static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
521 {
522 .port = {
523 .membase = (void*) io_p2v (UART1_PHYS),
524 .mapbase = UART1_PHYS,
525 .iotype = SERIAL_IO_MEM,
526 .irq = IRQ_UART1INTR,
527 .uartclk = 14745600/2,
528 .fifosize = 16,
529 .ops = &lh7a40x_uart_ops,
530 .flags = ASYNC_BOOT_AUTOCONF,
531 .line = 0,
532 },
533 },
534 {
535 .port = {
536 .membase = (void*) io_p2v (UART2_PHYS),
537 .mapbase = UART2_PHYS,
538 .iotype = SERIAL_IO_MEM,
539 .irq = IRQ_UART2INTR,
540 .uartclk = 14745600/2,
541 .fifosize = 16,
542 .ops = &lh7a40x_uart_ops,
543 .flags = ASYNC_BOOT_AUTOCONF,
544 .line = 1,
545 },
546 },
547 {
548 .port = {
549 .membase = (void*) io_p2v (UART3_PHYS),
550 .mapbase = UART3_PHYS,
551 .iotype = SERIAL_IO_MEM,
552 .irq = IRQ_UART3INTR,
553 .uartclk = 14745600/2,
554 .fifosize = 16,
555 .ops = &lh7a40x_uart_ops,
556 .flags = ASYNC_BOOT_AUTOCONF,
557 .line = 2,
558 },
559 },
560};
561
562#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
563# define LH7A40X_CONSOLE NULL
564#else
565# define LH7A40X_CONSOLE &lh7a40x_console
566
567
568static void lh7a40xuart_console_write (struct console* co,
569 const char* s,
570 unsigned int count)
571{
572 struct uart_port* port = &lh7a40x_ports[co->index].port;
573 unsigned int con = UR (port, UART_R_CON);
574 unsigned int inten = UR (port, UART_R_INTEN);
575
576
577 UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
578 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
579
580 for (; count-- > 0; ++s) {
581 while (UR (port, UART_R_STATUS) & nTxRdy)
582 ;
583 UR (port, UART_R_DATA) = *s;
584 if (*s == '\n') {
585 while ((UR (port, UART_R_STATUS) & TxBusy))
586 ;
587 UR (port, UART_R_DATA) = '\r';
588 }
589 }
590
591 /* Wait until all characters are sent */
592 while (UR (port, UART_R_STATUS) & TxBusy)
593 ;
594
595 /* Restore control and interrupt mask */
596 UR (port, UART_R_CON) = con;
597 UR (port, UART_R_INTEN) = inten;
598}
599
600static void __init lh7a40xuart_console_get_options (struct uart_port* port,
601 int* baud,
602 int* parity,
603 int* bits)
604{
605 if (UR (port, UART_R_CON) & UARTEN) {
606 unsigned int fcon = UR (port, UART_R_FCON);
607 unsigned int quot = UR (port, UART_R_BRCON) + 1;
608
609 switch (fcon & (PEN | EPS)) {
610 default: *parity = 'n'; break;
611 case PEN: *parity = 'o'; break;
612 case PEN | EPS: *parity = 'e'; break;
613 }
614
615 switch (fcon & WLEN) {
616 default:
617 case WLEN_8: *bits = 8; break;
618 case WLEN_7: *bits = 7; break;
619 case WLEN_6: *bits = 6; break;
620 case WLEN_5: *bits = 5; break;
621 }
622
623 *baud = port->uartclk/(16*quot);
624 }
625}
626
627static int __init lh7a40xuart_console_setup (struct console* co, char* options)
628{
629 struct uart_port* port;
630 int baud = 38400;
631 int bits = 8;
632 int parity = 'n';
633 int flow = 'n';
634
635 if (co->index >= DEV_NR) /* Bounds check on device number */
636 co->index = 0;
637 port = &lh7a40x_ports[co->index].port;
638
639 if (options)
640 uart_parse_options (options, &baud, &parity, &bits, &flow);
641 else
642 lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
643
644 return uart_set_options (port, co, baud, parity, bits, flow);
645}
646
647extern struct uart_driver lh7a40x_reg;
648static struct console lh7a40x_console = {
649 .name = "ttyAM",
650 .write = lh7a40xuart_console_write,
651 .device = uart_console_device,
652 .setup = lh7a40xuart_console_setup,
653 .flags = CON_PRINTBUFFER,
654 .index = -1,
655 .data = &lh7a40x_reg,
656};
657
658static int __init lh7a40xuart_console_init(void)
659{
660 register_console (&lh7a40x_console);
661 return 0;
662}
663
664console_initcall (lh7a40xuart_console_init);
665
666#endif
667
668static struct uart_driver lh7a40x_reg = {
669 .owner = THIS_MODULE,
670 .driver_name = "ttyAM",
671 .dev_name = "ttyAM",
672 .major = DEV_MAJOR,
673 .minor = DEV_MINOR,
674 .nr = DEV_NR,
675 .cons = LH7A40X_CONSOLE,
676};
677
678static int __init lh7a40xuart_init(void)
679{
680 int ret;
681
682 printk (KERN_INFO "serial: LH7A40X serial driver\n");
683
684 ret = uart_register_driver (&lh7a40x_reg);
685
686 if (ret == 0) {
687 int i;
688
689 for (i = 0; i < DEV_NR; i++)
690 uart_add_one_port (&lh7a40x_reg,
691 &lh7a40x_ports[i].port);
692 }
693 return ret;
694}
695
696static void __exit lh7a40xuart_exit(void)
697{
698 int i;
699
700 for (i = 0; i < DEV_NR; i++)
701 uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
702
703 uart_unregister_driver (&lh7a40x_reg);
704}
705
706module_init (lh7a40xuart_init);
707module_exit (lh7a40xuart_exit);
708
709MODULE_AUTHOR ("Marc Singer");
710MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
711MODULE_LICENSE ("GPL");