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