diff options
Diffstat (limited to 'drivers/serial/clps711x.c')
-rw-r--r-- | drivers/serial/clps711x.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c new file mode 100644 index 000000000000..16592fae47f3 --- /dev/null +++ b/drivers/serial/clps711x.c | |||
@@ -0,0 +1,609 @@ | |||
1 | /* | ||
2 | * linux/drivers/char/clps711x.c | ||
3 | * | ||
4 | * Driver for CLPS711x serial ports | ||
5 | * | ||
6 | * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. | ||
7 | * | ||
8 | * Copyright 1999 ARM Limited | ||
9 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | * | ||
25 | * $Id: clps711x.c,v 1.42 2002/07/28 10:03:28 rmk Exp $ | ||
26 | * | ||
27 | */ | ||
28 | #include <linux/config.h> | ||
29 | |||
30 | #if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||
31 | #define SUPPORT_SYSRQ | ||
32 | #endif | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/ioport.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/console.h> | ||
38 | #include <linux/sysrq.h> | ||
39 | #include <linux/spinlock.h> | ||
40 | #include <linux/device.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/hardware.h> | ||
47 | #include <asm/io.h> | ||
48 | #include <asm/irq.h> | ||
49 | #include <asm/hardware/clps7111.h> | ||
50 | |||
51 | #define UART_NR 2 | ||
52 | |||
53 | #define SERIAL_CLPS711X_MAJOR 204 | ||
54 | #define SERIAL_CLPS711X_MINOR 40 | ||
55 | #define SERIAL_CLPS711X_NR UART_NR | ||
56 | |||
57 | /* | ||
58 | * We use the relevant SYSCON register as a base address for these ports. | ||
59 | */ | ||
60 | #define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) | ||
61 | #define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) | ||
62 | #define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) | ||
63 | #define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) | ||
64 | |||
65 | #define TX_IRQ(port) ((port)->irq) | ||
66 | #define RX_IRQ(port) ((port)->irq + 1) | ||
67 | |||
68 | #define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) | ||
69 | |||
70 | #define tx_enabled(port) ((port)->unused[0]) | ||
71 | |||
72 | static void | ||
73 | clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop) | ||
74 | { | ||
75 | if (tx_enabled(port)) { | ||
76 | disable_irq(TX_IRQ(port)); | ||
77 | tx_enabled(port) = 0; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start) | ||
83 | { | ||
84 | if (!tx_enabled(port)) { | ||
85 | enable_irq(TX_IRQ(port)); | ||
86 | tx_enabled(port) = 1; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static void clps711xuart_stop_rx(struct uart_port *port) | ||
91 | { | ||
92 | disable_irq(RX_IRQ(port)); | ||
93 | } | ||
94 | |||
95 | static void clps711xuart_enable_ms(struct uart_port *port) | ||
96 | { | ||
97 | } | ||
98 | |||
99 | static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) | ||
100 | { | ||
101 | struct uart_port *port = dev_id; | ||
102 | struct tty_struct *tty = port->info->tty; | ||
103 | unsigned int status, ch, flg, ignored = 0; | ||
104 | |||
105 | status = clps_readl(SYSFLG(port)); | ||
106 | while (!(status & SYSFLG_URXFE)) { | ||
107 | ch = clps_readl(UARTDR(port)); | ||
108 | |||
109 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) | ||
110 | goto ignore_char; | ||
111 | port->icount.rx++; | ||
112 | |||
113 | flg = TTY_NORMAL; | ||
114 | |||
115 | /* | ||
116 | * Note that the error handling code is | ||
117 | * out of the main execution path | ||
118 | */ | ||
119 | if (ch & UART_ANY_ERR) | ||
120 | goto handle_error; | ||
121 | |||
122 | if (uart_handle_sysrq_char(port, ch, regs)) | ||
123 | goto ignore_char; | ||
124 | |||
125 | error_return: | ||
126 | tty_insert_flip_char(tty, ch, flg); | ||
127 | ignore_char: | ||
128 | status = clps_readl(SYSFLG(port)); | ||
129 | } | ||
130 | out: | ||
131 | tty_flip_buffer_push(tty); | ||
132 | return IRQ_HANDLED; | ||
133 | |||
134 | handle_error: | ||
135 | if (ch & UARTDR_PARERR) | ||
136 | port->icount.parity++; | ||
137 | else if (ch & UARTDR_FRMERR) | ||
138 | port->icount.frame++; | ||
139 | if (ch & UARTDR_OVERR) | ||
140 | port->icount.overrun++; | ||
141 | |||
142 | if (ch & port->ignore_status_mask) { | ||
143 | if (++ignored > 100) | ||
144 | goto out; | ||
145 | goto ignore_char; | ||
146 | } | ||
147 | ch &= port->read_status_mask; | ||
148 | |||
149 | if (ch & UARTDR_PARERR) | ||
150 | flg = TTY_PARITY; | ||
151 | else if (ch & UARTDR_FRMERR) | ||
152 | flg = TTY_FRAME; | ||
153 | |||
154 | if (ch & UARTDR_OVERR) { | ||
155 | /* | ||
156 | * CHECK: does overrun affect the current character? | ||
157 | * ASSUMPTION: it does not. | ||
158 | */ | ||
159 | tty_insert_flip_char(tty, ch, flg); | ||
160 | ch = 0; | ||
161 | flg = TTY_OVERRUN; | ||
162 | } | ||
163 | #ifdef SUPPORT_SYSRQ | ||
164 | port->sysrq = 0; | ||
165 | #endif | ||
166 | goto error_return; | ||
167 | } | ||
168 | |||
169 | static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) | ||
170 | { | ||
171 | struct uart_port *port = dev_id; | ||
172 | struct circ_buf *xmit = &port->info->xmit; | ||
173 | int count; | ||
174 | |||
175 | if (port->x_char) { | ||
176 | clps_writel(port->x_char, UARTDR(port)); | ||
177 | port->icount.tx++; | ||
178 | port->x_char = 0; | ||
179 | return IRQ_HANDLED; | ||
180 | } | ||
181 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { | ||
182 | clps711xuart_stop_tx(port, 0); | ||
183 | return IRQ_HANDLED; | ||
184 | } | ||
185 | |||
186 | count = port->fifosize >> 1; | ||
187 | do { | ||
188 | clps_writel(xmit->buf[xmit->tail], UARTDR(port)); | ||
189 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||
190 | port->icount.tx++; | ||
191 | if (uart_circ_empty(xmit)) | ||
192 | break; | ||
193 | } while (--count > 0); | ||
194 | |||
195 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
196 | uart_write_wakeup(port); | ||
197 | |||
198 | if (uart_circ_empty(xmit)) | ||
199 | clps711xuart_stop_tx(port, 0); | ||
200 | |||
201 | return IRQ_HANDLED; | ||
202 | } | ||
203 | |||
204 | static unsigned int clps711xuart_tx_empty(struct uart_port *port) | ||
205 | { | ||
206 | unsigned int status = clps_readl(SYSFLG(port)); | ||
207 | return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; | ||
208 | } | ||
209 | |||
210 | static unsigned int clps711xuart_get_mctrl(struct uart_port *port) | ||
211 | { | ||
212 | unsigned int port_addr; | ||
213 | unsigned int result = 0; | ||
214 | unsigned int status; | ||
215 | |||
216 | port_addr = SYSFLG(port); | ||
217 | if (port_addr == SYSFLG1) { | ||
218 | status = clps_readl(SYSFLG1); | ||
219 | if (status & SYSFLG1_DCD) | ||
220 | result |= TIOCM_CAR; | ||
221 | if (status & SYSFLG1_DSR) | ||
222 | result |= TIOCM_DSR; | ||
223 | if (status & SYSFLG1_CTS) | ||
224 | result |= TIOCM_CTS; | ||
225 | } | ||
226 | |||
227 | return result; | ||
228 | } | ||
229 | |||
230 | static void | ||
231 | clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) | ||
232 | { | ||
233 | } | ||
234 | |||
235 | static void clps711xuart_break_ctl(struct uart_port *port, int break_state) | ||
236 | { | ||
237 | unsigned long flags; | ||
238 | unsigned int ubrlcr; | ||
239 | |||
240 | spin_lock_irqsave(&port->lock, flags); | ||
241 | ubrlcr = clps_readl(UBRLCR(port)); | ||
242 | if (break_state == -1) | ||
243 | ubrlcr |= UBRLCR_BREAK; | ||
244 | else | ||
245 | ubrlcr &= ~UBRLCR_BREAK; | ||
246 | clps_writel(ubrlcr, UBRLCR(port)); | ||
247 | spin_unlock_irqrestore(&port->lock, flags); | ||
248 | } | ||
249 | |||
250 | static int clps711xuart_startup(struct uart_port *port) | ||
251 | { | ||
252 | unsigned int syscon; | ||
253 | int retval; | ||
254 | |||
255 | tx_enabled(port) = 1; | ||
256 | |||
257 | /* | ||
258 | * Allocate the IRQs | ||
259 | */ | ||
260 | retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, | ||
261 | "clps711xuart_tx", port); | ||
262 | if (retval) | ||
263 | return retval; | ||
264 | |||
265 | retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, | ||
266 | "clps711xuart_rx", port); | ||
267 | if (retval) { | ||
268 | free_irq(TX_IRQ(port), port); | ||
269 | return retval; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * enable the port | ||
274 | */ | ||
275 | syscon = clps_readl(SYSCON(port)); | ||
276 | syscon |= SYSCON_UARTEN; | ||
277 | clps_writel(syscon, SYSCON(port)); | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static void clps711xuart_shutdown(struct uart_port *port) | ||
283 | { | ||
284 | unsigned int ubrlcr, syscon; | ||
285 | |||
286 | /* | ||
287 | * Free the interrupt | ||
288 | */ | ||
289 | free_irq(TX_IRQ(port), port); /* TX interrupt */ | ||
290 | free_irq(RX_IRQ(port), port); /* RX interrupt */ | ||
291 | |||
292 | /* | ||
293 | * disable the port | ||
294 | */ | ||
295 | syscon = clps_readl(SYSCON(port)); | ||
296 | syscon &= ~SYSCON_UARTEN; | ||
297 | clps_writel(syscon, SYSCON(port)); | ||
298 | |||
299 | /* | ||
300 | * disable break condition and fifos | ||
301 | */ | ||
302 | ubrlcr = clps_readl(UBRLCR(port)); | ||
303 | ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); | ||
304 | clps_writel(ubrlcr, UBRLCR(port)); | ||
305 | } | ||
306 | |||
307 | static void | ||
308 | clps711xuart_set_termios(struct uart_port *port, struct termios *termios, | ||
309 | struct termios *old) | ||
310 | { | ||
311 | unsigned int ubrlcr, baud, quot; | ||
312 | unsigned long flags; | ||
313 | |||
314 | /* | ||
315 | * We don't implement CREAD. | ||
316 | */ | ||
317 | termios->c_cflag |= CREAD; | ||
318 | |||
319 | /* | ||
320 | * Ask the core to calculate the divisor for us. | ||
321 | */ | ||
322 | baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); | ||
323 | quot = uart_get_divisor(port, baud); | ||
324 | |||
325 | switch (termios->c_cflag & CSIZE) { | ||
326 | case CS5: | ||
327 | ubrlcr = UBRLCR_WRDLEN5; | ||
328 | break; | ||
329 | case CS6: | ||
330 | ubrlcr = UBRLCR_WRDLEN6; | ||
331 | break; | ||
332 | case CS7: | ||
333 | ubrlcr = UBRLCR_WRDLEN7; | ||
334 | break; | ||
335 | default: // CS8 | ||
336 | ubrlcr = UBRLCR_WRDLEN8; | ||
337 | break; | ||
338 | } | ||
339 | if (termios->c_cflag & CSTOPB) | ||
340 | ubrlcr |= UBRLCR_XSTOP; | ||
341 | if (termios->c_cflag & PARENB) { | ||
342 | ubrlcr |= UBRLCR_PRTEN; | ||
343 | if (!(termios->c_cflag & PARODD)) | ||
344 | ubrlcr |= UBRLCR_EVENPRT; | ||
345 | } | ||
346 | if (port->fifosize > 1) | ||
347 | ubrlcr |= UBRLCR_FIFOEN; | ||
348 | |||
349 | spin_lock_irqsave(&port->lock, flags); | ||
350 | |||
351 | /* | ||
352 | * Update the per-port timeout. | ||
353 | */ | ||
354 | uart_update_timeout(port, termios->c_cflag, baud); | ||
355 | |||
356 | port->read_status_mask = UARTDR_OVERR; | ||
357 | if (termios->c_iflag & INPCK) | ||
358 | port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; | ||
359 | |||
360 | /* | ||
361 | * Characters to ignore | ||
362 | */ | ||
363 | port->ignore_status_mask = 0; | ||
364 | if (termios->c_iflag & IGNPAR) | ||
365 | port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; | ||
366 | if (termios->c_iflag & IGNBRK) { | ||
367 | /* | ||
368 | * If we're ignoring parity and break indicators, | ||
369 | * ignore overruns to (for real raw support). | ||
370 | */ | ||
371 | if (termios->c_iflag & IGNPAR) | ||
372 | port->ignore_status_mask |= UARTDR_OVERR; | ||
373 | } | ||
374 | |||
375 | quot -= 1; | ||
376 | |||
377 | clps_writel(ubrlcr | quot, UBRLCR(port)); | ||
378 | |||
379 | spin_unlock_irqrestore(&port->lock, flags); | ||
380 | } | ||
381 | |||
382 | static const char *clps711xuart_type(struct uart_port *port) | ||
383 | { | ||
384 | return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Configure/autoconfigure the port. | ||
389 | */ | ||
390 | static void clps711xuart_config_port(struct uart_port *port, int flags) | ||
391 | { | ||
392 | if (flags & UART_CONFIG_TYPE) | ||
393 | port->type = PORT_CLPS711X; | ||
394 | } | ||
395 | |||
396 | static void clps711xuart_release_port(struct uart_port *port) | ||
397 | { | ||
398 | } | ||
399 | |||
400 | static int clps711xuart_request_port(struct uart_port *port) | ||
401 | { | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static struct uart_ops clps711x_pops = { | ||
406 | .tx_empty = clps711xuart_tx_empty, | ||
407 | .set_mctrl = clps711xuart_set_mctrl_null, | ||
408 | .get_mctrl = clps711xuart_get_mctrl, | ||
409 | .stop_tx = clps711xuart_stop_tx, | ||
410 | .start_tx = clps711xuart_start_tx, | ||
411 | .stop_rx = clps711xuart_stop_rx, | ||
412 | .enable_ms = clps711xuart_enable_ms, | ||
413 | .break_ctl = clps711xuart_break_ctl, | ||
414 | .startup = clps711xuart_startup, | ||
415 | .shutdown = clps711xuart_shutdown, | ||
416 | .set_termios = clps711xuart_set_termios, | ||
417 | .type = clps711xuart_type, | ||
418 | .config_port = clps711xuart_config_port, | ||
419 | .release_port = clps711xuart_release_port, | ||
420 | .request_port = clps711xuart_request_port, | ||
421 | }; | ||
422 | |||
423 | static struct uart_port clps711x_ports[UART_NR] = { | ||
424 | { | ||
425 | .iobase = SYSCON1, | ||
426 | .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ | ||
427 | .uartclk = 3686400, | ||
428 | .fifosize = 16, | ||
429 | .ops = &clps711x_pops, | ||
430 | .line = 0, | ||
431 | .flags = ASYNC_BOOT_AUTOCONF, | ||
432 | }, | ||
433 | { | ||
434 | .iobase = SYSCON2, | ||
435 | .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ | ||
436 | .uartclk = 3686400, | ||
437 | .fifosize = 16, | ||
438 | .ops = &clps711x_pops, | ||
439 | .line = 1, | ||
440 | .flags = ASYNC_BOOT_AUTOCONF, | ||
441 | } | ||
442 | }; | ||
443 | |||
444 | #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE | ||
445 | /* | ||
446 | * Print a string to the serial port trying not to disturb | ||
447 | * any possible real use of the port... | ||
448 | * | ||
449 | * The console_lock must be held when we get here. | ||
450 | * | ||
451 | * Note that this is called with interrupts already disabled | ||
452 | */ | ||
453 | static void | ||
454 | clps711xuart_console_write(struct console *co, const char *s, | ||
455 | unsigned int count) | ||
456 | { | ||
457 | struct uart_port *port = clps711x_ports + co->index; | ||
458 | unsigned int status, syscon; | ||
459 | int i; | ||
460 | |||
461 | /* | ||
462 | * Ensure that the port is enabled. | ||
463 | */ | ||
464 | syscon = clps_readl(SYSCON(port)); | ||
465 | clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); | ||
466 | |||
467 | /* | ||
468 | * Now, do each character | ||
469 | */ | ||
470 | for (i = 0; i < count; i++) { | ||
471 | do { | ||
472 | status = clps_readl(SYSFLG(port)); | ||
473 | } while (status & SYSFLG_UTXFF); | ||
474 | clps_writel(s[i], UARTDR(port)); | ||
475 | if (s[i] == '\n') { | ||
476 | do { | ||
477 | status = clps_readl(SYSFLG(port)); | ||
478 | } while (status & SYSFLG_UTXFF); | ||
479 | clps_writel('\r', UARTDR(port)); | ||
480 | } | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * Finally, wait for transmitter to become empty | ||
485 | * and restore the uart state. | ||
486 | */ | ||
487 | do { | ||
488 | status = clps_readl(SYSFLG(port)); | ||
489 | } while (status & SYSFLG_UBUSY); | ||
490 | |||
491 | clps_writel(syscon, SYSCON(port)); | ||
492 | } | ||
493 | |||
494 | static void __init | ||
495 | clps711xuart_console_get_options(struct uart_port *port, int *baud, | ||
496 | int *parity, int *bits) | ||
497 | { | ||
498 | if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { | ||
499 | unsigned int ubrlcr, quot; | ||
500 | |||
501 | ubrlcr = clps_readl(UBRLCR(port)); | ||
502 | |||
503 | *parity = 'n'; | ||
504 | if (ubrlcr & UBRLCR_PRTEN) { | ||
505 | if (ubrlcr & UBRLCR_EVENPRT) | ||
506 | *parity = 'e'; | ||
507 | else | ||
508 | *parity = 'o'; | ||
509 | } | ||
510 | |||
511 | if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) | ||
512 | *bits = 7; | ||
513 | else | ||
514 | *bits = 8; | ||
515 | |||
516 | quot = ubrlcr & UBRLCR_BAUD_MASK; | ||
517 | *baud = port->uartclk / (16 * (quot + 1)); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | static int __init clps711xuart_console_setup(struct console *co, char *options) | ||
522 | { | ||
523 | struct uart_port *port; | ||
524 | int baud = 38400; | ||
525 | int bits = 8; | ||
526 | int parity = 'n'; | ||
527 | int flow = 'n'; | ||
528 | |||
529 | /* | ||
530 | * Check whether an invalid uart number has been specified, and | ||
531 | * if so, search for the first available port that does have | ||
532 | * console support. | ||
533 | */ | ||
534 | port = uart_get_console(clps711x_ports, UART_NR, co); | ||
535 | |||
536 | if (options) | ||
537 | uart_parse_options(options, &baud, &parity, &bits, &flow); | ||
538 | else | ||
539 | clps711xuart_console_get_options(port, &baud, &parity, &bits); | ||
540 | |||
541 | return uart_set_options(port, co, baud, parity, bits, flow); | ||
542 | } | ||
543 | |||
544 | extern struct uart_driver clps711x_reg; | ||
545 | static struct console clps711x_console = { | ||
546 | .name = "ttyCL", | ||
547 | .write = clps711xuart_console_write, | ||
548 | .device = uart_console_device, | ||
549 | .setup = clps711xuart_console_setup, | ||
550 | .flags = CON_PRINTBUFFER, | ||
551 | .index = -1, | ||
552 | .data = &clps711x_reg, | ||
553 | }; | ||
554 | |||
555 | static int __init clps711xuart_console_init(void) | ||
556 | { | ||
557 | register_console(&clps711x_console); | ||
558 | return 0; | ||
559 | } | ||
560 | console_initcall(clps711xuart_console_init); | ||
561 | |||
562 | #define CLPS711X_CONSOLE &clps711x_console | ||
563 | #else | ||
564 | #define CLPS711X_CONSOLE NULL | ||
565 | #endif | ||
566 | |||
567 | static struct uart_driver clps711x_reg = { | ||
568 | .driver_name = "ttyCL", | ||
569 | .dev_name = "ttyCL", | ||
570 | .major = SERIAL_CLPS711X_MAJOR, | ||
571 | .minor = SERIAL_CLPS711X_MINOR, | ||
572 | .nr = UART_NR, | ||
573 | |||
574 | .cons = CLPS711X_CONSOLE, | ||
575 | }; | ||
576 | |||
577 | static int __init clps711xuart_init(void) | ||
578 | { | ||
579 | int ret, i; | ||
580 | |||
581 | printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.42 $\n"); | ||
582 | |||
583 | ret = uart_register_driver(&clps711x_reg); | ||
584 | if (ret) | ||
585 | return ret; | ||
586 | |||
587 | for (i = 0; i < UART_NR; i++) | ||
588 | uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); | ||
589 | |||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static void __exit clps711xuart_exit(void) | ||
594 | { | ||
595 | int i; | ||
596 | |||
597 | for (i = 0; i < UART_NR; i++) | ||
598 | uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); | ||
599 | |||
600 | uart_unregister_driver(&clps711x_reg); | ||
601 | } | ||
602 | |||
603 | module_init(clps711xuart_init); | ||
604 | module_exit(clps711xuart_exit); | ||
605 | |||
606 | MODULE_AUTHOR("Deep Blue Solutions Ltd"); | ||
607 | MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.42 $"); | ||
608 | MODULE_LICENSE("GPL"); | ||
609 | MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR); | ||