[PATCH] sched cleanups
[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)
116 {
117         BIT_CLR (port, UART_R_INTEN, TxInt);
118 }
119
120 static void lh7a40xuart_start_tx (struct uart_port* port)
121 {
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
129 static void lh7a40xuart_stop_rx (struct uart_port* port)
130 {
131         BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
132 }
133
134 static void lh7a40xuart_enable_ms (struct uart_port* port)
135 {
136         BIT_SET (port, UART_R_INTEN, ModemInt);
137 }
138
139 static void
140 #ifdef SUPPORT_SYSRQ
141 lh7a40xuart_rx_chars (struct uart_port* port, struct pt_regs* regs)
142 #else
143 lh7a40xuart_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
164                 if (unlikely(data & RxError)) { /* Quick check, short-circuit */
165                         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
192                 uart_insert_char(port, data, RxOverrunError, data, flag);
193         }
194         tty_flip_buffer_push (tty);
195         return;
196 }
197
198 static 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)) {
210                 lh7a40xuart_stop_tx (port);
211                 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))
232                 lh7a40xuart_stop_tx (port);
233 }
234
235 static 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
260 static 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
289 static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
290 {
291         return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
292 }
293
294 static 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
309 static 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
328 static 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
340 static 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
362 static 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
369 static 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
450 static const char* lh7a40xuart_type (struct uart_port* port)
451 {
452         return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
453 }
454
455 static void lh7a40xuart_release_port (struct uart_port* port)
456 {
457         release_mem_region (port->mapbase, UART_REG_SIZE);
458 }
459
460 static 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
467 static 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
475 static 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
489 static 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
508 static 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
556 static 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
588 static 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
615 static 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
635 extern struct uart_driver lh7a40x_reg;
636 static 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
646 static int __init lh7a40xuart_console_init(void)
647 {
648         register_console (&lh7a40x_console);
649         return 0;
650 }
651
652 console_initcall (lh7a40xuart_console_init);
653
654 #endif
655
656 static 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
666 static 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
684 static 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
694 module_init (lh7a40xuart_init);
695 module_exit (lh7a40xuart_exit);
696
697 MODULE_AUTHOR ("Marc Singer");
698 MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
699 MODULE_LICENSE ("GPL");