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