blob: d01dbe5da3b96d395caf8c83a9b923d1325833a8 [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
Russell Kingb129a8c2005-08-31 10:12:14 +0100115static void lh7a40xuart_stop_tx (struct uart_port* port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
117 BIT_CLR (port, UART_R_INTEN, TxInt);
118}
119
Russell Kingb129a8c2005-08-31 10:12:14 +0100120static void lh7a40xuart_start_tx (struct uart_port* port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
122 BIT_SET (port, UART_R_INTEN, TxInt);
123
124 /* *** FIXME: do I need to check for startup of the
125 transmitter? The old driver did, but AMBA
126 doesn't . */
127}
128
129static void lh7a40xuart_stop_rx (struct uart_port* port)
130{
131 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
132}
133
134static void lh7a40xuart_enable_ms (struct uart_port* port)
135{
136 BIT_SET (port, UART_R_INTEN, ModemInt);
137}
138
139static void
140#ifdef SUPPORT_SYSRQ
141lh7a40xuart_rx_chars (struct uart_port* port, struct pt_regs* regs)
142#else
143lh7a40xuart_rx_chars (struct uart_port* port)
144#endif
145{
146 struct tty_struct* tty = port->info->tty;
147 int cbRxMax = 256; /* (Gross) limit on receive */
148 unsigned int data, flag;/* Received data and status */
149
150 while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
151 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
152 if (tty->low_latency)
153 tty_flip_buffer_push(tty);
154 /*
155 * If this failed then we will throw away the
156 * bytes but must do so to clear interrupts
157 */
158 }
159
160 data = UR (port, UART_R_DATA);
161 flag = TTY_NORMAL;
162 ++port->icount.rx;
163
Russell King45849282005-04-26 15:29:44 +0100164 if (unlikely(data & RxError)) { /* Quick check, short-circuit */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 if (data & RxBreak) {
166 data &= ~(RxFramingError | RxParityError);
167 ++port->icount.brk;
168 if (uart_handle_break (port))
169 continue;
170 }
171 else if (data & RxParityError)
172 ++port->icount.parity;
173 else if (data & RxFramingError)
174 ++port->icount.frame;
175 if (data & RxOverrunError)
176 ++port->icount.overrun;
177
178 /* Mask by termios, leave Rx'd byte */
179 data &= port->read_status_mask | 0xff;
180
181 if (data & RxBreak)
182 flag = TTY_BREAK;
183 else if (data & RxParityError)
184 flag = TTY_PARITY;
185 else if (data & RxFramingError)
186 flag = TTY_FRAME;
187 }
188
189 if (uart_handle_sysrq_char (port, (unsigned char) data, regs))
190 continue;
191
Russell King05ab3012005-05-09 23:21:59 +0100192 uart_insert_char(port, data, RxOverrunError, data, flag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 }
194 tty_flip_buffer_push (tty);
195 return;
196}
197
198static void lh7a40xuart_tx_chars (struct uart_port* port)
199{
200 struct circ_buf* xmit = &port->info->xmit;
201 int cbTxMax = port->fifosize;
202
203 if (port->x_char) {
204 UR (port, UART_R_DATA) = port->x_char;
205 ++port->icount.tx;
206 port->x_char = 0;
207 return;
208 }
209 if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
Russell King1cd98552005-09-06 23:14:59 +0100210 lh7a40xuart_stop_tx (port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 return;
212 }
213
214 /* Unlike the AMBA UART, the lh7a40x UART does not guarantee
215 that at least half of the FIFO is empty. Instead, we check
216 status for every character. Using the AMBA method causes
217 the transmitter to drop characters. */
218
219 do {
220 UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
221 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
222 ++port->icount.tx;
223 if (uart_circ_empty(xmit))
224 break;
225 } while (!(UR (port, UART_R_STATUS) & nTxRdy)
226 && cbTxMax--);
227
228 if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
229 uart_write_wakeup (port);
230
231 if (uart_circ_empty (xmit))
Russell King1cd98552005-09-06 23:14:59 +0100232 lh7a40xuart_stop_tx (port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233}
234
235static void lh7a40xuart_modem_status (struct uart_port* port)
236{
237 unsigned int status = UR (port, UART_R_STATUS);
238 unsigned int delta
239 = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
240
241 BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
242
243 if (!delta) /* Only happens if we missed 2 transitions */
244 return;
245
246 ((struct uart_port_lh7a40x*) port)->statusPrev = status;
247
248 if (delta & DCD)
249 uart_handle_dcd_change (port, status & DCD);
250
251 if (delta & DSR)
252 ++port->icount.dsr;
253
254 if (delta & CTS)
255 uart_handle_cts_change (port, status & CTS);
256
257 wake_up_interruptible (&port->info->delta_msr_wait);
258}
259
260static irqreturn_t lh7a40xuart_int (int irq, void* dev_id,
261 struct pt_regs* regs)
262{
263 struct uart_port* port = dev_id;
264 unsigned int cLoopLimit = ISR_LOOP_LIMIT;
265 unsigned int isr = UR (port, UART_R_ISR);
266
267
268 do {
269 if (isr & (RxInt | RxTimeoutInt))
270#ifdef SUPPORT_SYSRQ
271 lh7a40xuart_rx_chars(port, regs);
272#else
273 lh7a40xuart_rx_chars(port);
274#endif
275 if (isr & ModemInt)
276 lh7a40xuart_modem_status (port);
277 if (isr & TxInt)
278 lh7a40xuart_tx_chars (port);
279
280 if (--cLoopLimit == 0)
281 break;
282
283 isr = UR (port, UART_R_ISR);
284 } while (isr & (RxInt | TxInt | RxTimeoutInt));
285
286 return IRQ_HANDLED;
287}
288
289static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
290{
291 return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
292}
293
294static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
295{
296 unsigned int result = 0;
297 unsigned int status = UR (port, UART_R_STATUS);
298
299 if (status & DCD)
300 result |= TIOCM_CAR;
301 if (status & DSR)
302 result |= TIOCM_DSR;
303 if (status & CTS)
304 result |= TIOCM_CTS;
305
306 return result;
307}
308
309static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
310{
311 /* None of the ports supports DTR. UART1 supports RTS through GPIO. */
312 /* Note, kernel appears to be setting DTR and RTS on console. */
313
314 /* *** FIXME: this deserves more work. There's some work in
315 tracing all of the IO pins. */
316#if 0
317 if( port->mapbase == UART1_PHYS) {
318 gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
319
320 if (mctrl & TIOCM_RTS)
321 gpio->pbdr &= ~GPIOB_UART1_RTS;
322 else
323 gpio->pbdr |= GPIOB_UART1_RTS;
324 }
325#endif
326}
327
328static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
329{
330 unsigned long flags;
331
332 spin_lock_irqsave(&port->lock, flags);
333 if (break_state == -1)
334 BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
335 else
336 BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
337 spin_unlock_irqrestore(&port->lock, flags);
338}
339
340static int lh7a40xuart_startup (struct uart_port* port)
341{
342 int retval;
343
344 retval = request_irq (port->irq, lh7a40xuart_int, 0,
345 "serial_lh7a40x", port);
346 if (retval)
347 return retval;
348
349 /* Initial modem control-line settings */
350 ((struct uart_port_lh7a40x*) port)->statusPrev
351 = UR (port, UART_R_STATUS);
352
353 /* There is presently no configuration option to enable IR.
354 Thus, we always disable it. */
355
356 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
357 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
358
359 return 0;
360}
361
362static void lh7a40xuart_shutdown (struct uart_port* port)
363{
364 free_irq (port->irq, port);
365 BIT_CLR (port, UART_R_FCON, BRK | FEN);
366 BIT_CLR (port, UART_R_CON, UARTEN);
367}
368
369static void lh7a40xuart_set_termios (struct uart_port* port,
370 struct termios* termios,
371 struct termios* old)
372{
373 unsigned int con;
374 unsigned int inten;
375 unsigned int fcon;
376 unsigned long flags;
377 unsigned int baud;
378 unsigned int quot;
379
380 baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
381 quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
382
383 switch (termios->c_cflag & CSIZE) {
384 case CS5:
385 fcon = WLEN_5;
386 break;
387 case CS6:
388 fcon = WLEN_6;
389 break;
390 case CS7:
391 fcon = WLEN_7;
392 break;
393 case CS8:
394 default:
395 fcon = WLEN_8;
396 break;
397 }
398 if (termios->c_cflag & CSTOPB)
399 fcon |= STP2;
400 if (termios->c_cflag & PARENB) {
401 fcon |= PEN;
402 if (!(termios->c_cflag & PARODD))
403 fcon |= EPS;
404 }
405 if (port->fifosize > 1)
406 fcon |= FEN;
407
408 spin_lock_irqsave (&port->lock, flags);
409
410 uart_update_timeout (port, termios->c_cflag, baud);
411
412 port->read_status_mask = RxOverrunError;
413 if (termios->c_iflag & INPCK)
414 port->read_status_mask |= RxFramingError | RxParityError;
415 if (termios->c_iflag & (BRKINT | PARMRK))
416 port->read_status_mask |= RxBreak;
417
418 /* Figure mask for status we ignore */
419 port->ignore_status_mask = 0;
420 if (termios->c_iflag & IGNPAR)
421 port->ignore_status_mask |= RxFramingError | RxParityError;
422 if (termios->c_iflag & IGNBRK) {
423 port->ignore_status_mask |= RxBreak;
424 /* Ignore overrun when ignorning parity */
425 /* *** FIXME: is this in the right place? */
426 if (termios->c_iflag & IGNPAR)
427 port->ignore_status_mask |= RxOverrunError;
428 }
429
430 /* Ignore all receive errors when receive disabled */
431 if ((termios->c_cflag & CREAD) == 0)
432 port->ignore_status_mask |= RxError;
433
434 con = UR (port, UART_R_CON);
435 inten = (UR (port, UART_R_INTEN) & ~ModemInt);
436
437 if (UART_ENABLE_MS (port, termios->c_cflag))
438 inten |= ModemInt;
439
440 BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
441 UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
442 UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
443 UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
444 UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
445 UR (port, UART_R_CON) = con; /* Restore UART mode */
446
447 spin_unlock_irqrestore(&port->lock, flags);
448}
449
450static const char* lh7a40xuart_type (struct uart_port* port)
451{
452 return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
453}
454
455static void lh7a40xuart_release_port (struct uart_port* port)
456{
457 release_mem_region (port->mapbase, UART_REG_SIZE);
458}
459
460static int lh7a40xuart_request_port (struct uart_port* port)
461{
462 return request_mem_region (port->mapbase, UART_REG_SIZE,
463 "serial_lh7a40x") != NULL
464 ? 0 : -EBUSY;
465}
466
467static void lh7a40xuart_config_port (struct uart_port* port, int flags)
468{
469 if (flags & UART_CONFIG_TYPE) {
470 port->type = PORT_LH7A40X;
471 lh7a40xuart_request_port (port);
472 }
473}
474
475static int lh7a40xuart_verify_port (struct uart_port* port,
476 struct serial_struct* ser)
477{
478 int ret = 0;
479
480 if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
481 ret = -EINVAL;
482 if (ser->irq < 0 || ser->irq >= NR_IRQS)
483 ret = -EINVAL;
484 if (ser->baud_base < 9600) /* *** FIXME: is this true? */
485 ret = -EINVAL;
486 return ret;
487}
488
489static struct uart_ops lh7a40x_uart_ops = {
490 .tx_empty = lh7a40xuart_tx_empty,
491 .set_mctrl = lh7a40xuart_set_mctrl,
492 .get_mctrl = lh7a40xuart_get_mctrl,
493 .stop_tx = lh7a40xuart_stop_tx,
494 .start_tx = lh7a40xuart_start_tx,
495 .stop_rx = lh7a40xuart_stop_rx,
496 .enable_ms = lh7a40xuart_enable_ms,
497 .break_ctl = lh7a40xuart_break_ctl,
498 .startup = lh7a40xuart_startup,
499 .shutdown = lh7a40xuart_shutdown,
500 .set_termios = lh7a40xuart_set_termios,
501 .type = lh7a40xuart_type,
502 .release_port = lh7a40xuart_release_port,
503 .request_port = lh7a40xuart_request_port,
504 .config_port = lh7a40xuart_config_port,
505 .verify_port = lh7a40xuart_verify_port,
506};
507
508static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
509 {
510 .port = {
511 .membase = (void*) io_p2v (UART1_PHYS),
512 .mapbase = UART1_PHYS,
513 .iotype = SERIAL_IO_MEM,
514 .irq = IRQ_UART1INTR,
515 .uartclk = 14745600/2,
516 .fifosize = 16,
517 .ops = &lh7a40x_uart_ops,
518 .flags = ASYNC_BOOT_AUTOCONF,
519 .line = 0,
520 },
521 },
522 {
523 .port = {
524 .membase = (void*) io_p2v (UART2_PHYS),
525 .mapbase = UART2_PHYS,
526 .iotype = SERIAL_IO_MEM,
527 .irq = IRQ_UART2INTR,
528 .uartclk = 14745600/2,
529 .fifosize = 16,
530 .ops = &lh7a40x_uart_ops,
531 .flags = ASYNC_BOOT_AUTOCONF,
532 .line = 1,
533 },
534 },
535 {
536 .port = {
537 .membase = (void*) io_p2v (UART3_PHYS),
538 .mapbase = UART3_PHYS,
539 .iotype = SERIAL_IO_MEM,
540 .irq = IRQ_UART3INTR,
541 .uartclk = 14745600/2,
542 .fifosize = 16,
543 .ops = &lh7a40x_uart_ops,
544 .flags = ASYNC_BOOT_AUTOCONF,
545 .line = 2,
546 },
547 },
548};
549
550#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
551# define LH7A40X_CONSOLE NULL
552#else
553# define LH7A40X_CONSOLE &lh7a40x_console
554
555
556static void lh7a40xuart_console_write (struct console* co,
557 const char* s,
558 unsigned int count)
559{
560 struct uart_port* port = &lh7a40x_ports[co->index].port;
561 unsigned int con = UR (port, UART_R_CON);
562 unsigned int inten = UR (port, UART_R_INTEN);
563
564
565 UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
566 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
567
568 for (; count-- > 0; ++s) {
569 while (UR (port, UART_R_STATUS) & nTxRdy)
570 ;
571 UR (port, UART_R_DATA) = *s;
572 if (*s == '\n') {
573 while ((UR (port, UART_R_STATUS) & TxBusy))
574 ;
575 UR (port, UART_R_DATA) = '\r';
576 }
577 }
578
579 /* Wait until all characters are sent */
580 while (UR (port, UART_R_STATUS) & TxBusy)
581 ;
582
583 /* Restore control and interrupt mask */
584 UR (port, UART_R_CON) = con;
585 UR (port, UART_R_INTEN) = inten;
586}
587
588static void __init lh7a40xuart_console_get_options (struct uart_port* port,
589 int* baud,
590 int* parity,
591 int* bits)
592{
593 if (UR (port, UART_R_CON) & UARTEN) {
594 unsigned int fcon = UR (port, UART_R_FCON);
595 unsigned int quot = UR (port, UART_R_BRCON) + 1;
596
597 switch (fcon & (PEN | EPS)) {
598 default: *parity = 'n'; break;
599 case PEN: *parity = 'o'; break;
600 case PEN | EPS: *parity = 'e'; break;
601 }
602
603 switch (fcon & WLEN) {
604 default:
605 case WLEN_8: *bits = 8; break;
606 case WLEN_7: *bits = 7; break;
607 case WLEN_6: *bits = 6; break;
608 case WLEN_5: *bits = 5; break;
609 }
610
611 *baud = port->uartclk/(16*quot);
612 }
613}
614
615static int __init lh7a40xuart_console_setup (struct console* co, char* options)
616{
617 struct uart_port* port;
618 int baud = 38400;
619 int bits = 8;
620 int parity = 'n';
621 int flow = 'n';
622
623 if (co->index >= DEV_NR) /* Bounds check on device number */
624 co->index = 0;
625 port = &lh7a40x_ports[co->index].port;
626
627 if (options)
628 uart_parse_options (options, &baud, &parity, &bits, &flow);
629 else
630 lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
631
632 return uart_set_options (port, co, baud, parity, bits, flow);
633}
634
Vincent Sanders2d934862005-09-14 22:36:03 +0100635static struct uart_driver lh7a40x_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636static struct console lh7a40x_console = {
637 .name = "ttyAM",
638 .write = lh7a40xuart_console_write,
639 .device = uart_console_device,
640 .setup = lh7a40xuart_console_setup,
641 .flags = CON_PRINTBUFFER,
642 .index = -1,
643 .data = &lh7a40x_reg,
644};
645
646static int __init lh7a40xuart_console_init(void)
647{
648 register_console (&lh7a40x_console);
649 return 0;
650}
651
652console_initcall (lh7a40xuart_console_init);
653
654#endif
655
656static struct uart_driver lh7a40x_reg = {
657 .owner = THIS_MODULE,
658 .driver_name = "ttyAM",
659 .dev_name = "ttyAM",
660 .major = DEV_MAJOR,
661 .minor = DEV_MINOR,
662 .nr = DEV_NR,
663 .cons = LH7A40X_CONSOLE,
664};
665
666static int __init lh7a40xuart_init(void)
667{
668 int ret;
669
670 printk (KERN_INFO "serial: LH7A40X serial driver\n");
671
672 ret = uart_register_driver (&lh7a40x_reg);
673
674 if (ret == 0) {
675 int i;
676
677 for (i = 0; i < DEV_NR; i++)
678 uart_add_one_port (&lh7a40x_reg,
679 &lh7a40x_ports[i].port);
680 }
681 return ret;
682}
683
684static void __exit lh7a40xuart_exit(void)
685{
686 int i;
687
688 for (i = 0; i < DEV_NR; i++)
689 uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
690
691 uart_unregister_driver (&lh7a40x_reg);
692}
693
694module_init (lh7a40xuart_init);
695module_exit (lh7a40xuart_exit);
696
697MODULE_AUTHOR ("Marc Singer");
698MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
699MODULE_LICENSE ("GPL");