ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / serial / clps711x.c
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 #include <linux/module.h>
30 #include <linux/tty.h>
31 #include <linux/ioport.h>
32 #include <linux/init.h>
33 #include <linux/serial.h>
34 #include <linux/console.h>
35 #include <linux/sysrq.h>
36 #include <linux/spinlock.h>
37 #include <linux/device.h>
38
39 #include <asm/hardware.h>
40 #include <asm/io.h>
41 #include <asm/irq.h>
42
43 #if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
44 #define SUPPORT_SYSRQ
45 #endif
46
47 #include <linux/serial_core.h>
48
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->flip.flag_buf_ptr++ = flg;
127                 *tty->flip.char_buf_ptr++ = ch;
128                 tty->flip.count++;
129         ignore_char:
130                 status = clps_readl(SYSFLG(port));
131         }
132  out:
133         tty_flip_buffer_push(tty);
134         return IRQ_HANDLED;
135
136  handle_error:
137         if (ch & UARTDR_PARERR)
138                 port->icount.parity++;
139         else if (ch & UARTDR_FRMERR)
140                 port->icount.frame++;
141         if (ch & UARTDR_OVERR)
142                 port->icount.overrun++;
143
144         if (ch & port->ignore_status_mask) {
145                 if (++ignored > 100)
146                         goto out;
147                 goto ignore_char;
148         }
149         ch &= port->read_status_mask;
150
151         if (ch & UARTDR_PARERR)
152                 flg = TTY_PARITY;
153         else if (ch & UARTDR_FRMERR)
154                 flg = TTY_FRAME;
155
156         if (ch & UARTDR_OVERR) {
157                 /*
158                  * CHECK: does overrun affect the current character?
159                  * ASSUMPTION: it does not.
160                  */
161                 *tty->flip.flag_buf_ptr++ = flg;
162                 *tty->flip.char_buf_ptr++ = ch;
163                 tty->flip.count++;
164                 if (tty->flip.count >= TTY_FLIPBUF_SIZE)
165                         goto ignore_char;
166                 ch = 0;
167                 flg = TTY_OVERRUN;
168         }
169 #ifdef SUPPORT_SYSRQ
170         port->sysrq = 0;
171 #endif
172         goto error_return;
173 }
174
175 static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
176 {
177         struct uart_port *port = dev_id;
178         struct circ_buf *xmit = &port->info->xmit;
179         int count;
180
181         if (port->x_char) {
182                 clps_writel(port->x_char, UARTDR(port));
183                 port->icount.tx++;
184                 port->x_char = 0;
185                 return IRQ_HANDLED;
186         }
187         if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
188                 clps711xuart_stop_tx(port, 0);
189                 return IRQ_HANDLED;
190         }
191
192         count = port->fifosize >> 1;
193         do {
194                 clps_writel(xmit->buf[xmit->tail], UARTDR(port));
195                 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
196                 port->icount.tx++;
197                 if (uart_circ_empty(xmit))
198                         break;
199         } while (--count > 0);
200
201         if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
202                 uart_write_wakeup(port);
203
204         if (uart_circ_empty(xmit))
205                 clps711xuart_stop_tx(port, 0);
206
207         return IRQ_HANDLED;
208 }
209
210 static unsigned int clps711xuart_tx_empty(struct uart_port *port)
211 {
212         unsigned int status = clps_readl(SYSFLG(port));
213         return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
214 }
215
216 static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
217 {
218         unsigned int port_addr;
219         unsigned int result = 0;
220         unsigned int status;
221
222         port_addr = SYSFLG(port);
223         if (port_addr == SYSFLG1) {
224                 status = clps_readl(SYSFLG1);
225                 if (status & SYSFLG1_DCD)
226                         result |= TIOCM_CAR;
227                 if (status & SYSFLG1_DSR)
228                         result |= TIOCM_DSR;
229                 if (status & SYSFLG1_CTS)
230                         result |= TIOCM_CTS;
231         }
232
233         return result;
234 }
235
236 static void
237 clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
238 {
239 }
240
241 static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
242 {
243         unsigned long flags;
244         unsigned int ubrlcr;
245
246         spin_lock_irqsave(&port->lock, flags);
247         ubrlcr = clps_readl(UBRLCR(port));
248         if (break_state == -1)
249                 ubrlcr |= UBRLCR_BREAK;
250         else
251                 ubrlcr &= ~UBRLCR_BREAK;
252         clps_writel(ubrlcr, UBRLCR(port));
253         spin_unlock_irqrestore(&port->lock, flags);
254 }
255
256 static int clps711xuart_startup(struct uart_port *port)
257 {
258         unsigned int syscon;
259         int retval;
260
261         tx_enabled(port) = 1;
262
263         /*
264          * Allocate the IRQs
265          */
266         retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
267                              "clps711xuart_tx", port);
268         if (retval)
269                 return retval;
270
271         retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
272                              "clps711xuart_rx", port);
273         if (retval) {
274                 free_irq(TX_IRQ(port), port);
275                 return retval;
276         }
277
278         /*
279          * enable the port
280          */
281         syscon = clps_readl(SYSCON(port));
282         syscon |= SYSCON_UARTEN;
283         clps_writel(syscon, SYSCON(port));
284
285         return 0;
286 }
287
288 static void clps711xuart_shutdown(struct uart_port *port)
289 {
290         unsigned int ubrlcr, syscon;
291
292         /*
293          * Free the interrupt
294          */
295         free_irq(TX_IRQ(port), port);   /* TX interrupt */
296         free_irq(RX_IRQ(port), port);   /* RX interrupt */
297
298         /*
299          * disable the port
300          */
301         syscon = clps_readl(SYSCON(port));
302         syscon &= ~SYSCON_UARTEN;
303         clps_writel(syscon, SYSCON(port));
304
305         /*
306          * disable break condition and fifos
307          */
308         ubrlcr = clps_readl(UBRLCR(port));
309         ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
310         clps_writel(ubrlcr, UBRLCR(port));
311 }
312
313 static void
314 clps711xuart_set_termios(struct uart_port *port, struct termios *termios,
315                          struct termios *old)
316 {
317         unsigned int ubrlcr, baud, quot;
318         unsigned long flags;
319
320         /*
321          * We don't implement CREAD.
322          */
323         termios->c_cflag |= CREAD;
324
325         /*
326          * Ask the core to calculate the divisor for us.
327          */
328         baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
329         quot = uart_get_divisor(port, baud);
330
331         switch (termios->c_cflag & CSIZE) {
332         case CS5:
333                 ubrlcr = UBRLCR_WRDLEN5;
334                 break;
335         case CS6:
336                 ubrlcr = UBRLCR_WRDLEN6;
337                 break;
338         case CS7:
339                 ubrlcr = UBRLCR_WRDLEN7;
340                 break;
341         default: // CS8
342                 ubrlcr = UBRLCR_WRDLEN8;
343                 break;
344         }
345         if (termios->c_cflag & CSTOPB)
346                 ubrlcr |= UBRLCR_XSTOP;
347         if (termios->c_cflag & PARENB) {
348                 ubrlcr |= UBRLCR_PRTEN;
349                 if (!(termios->c_cflag & PARODD))
350                         ubrlcr |= UBRLCR_EVENPRT;
351         }
352         if (port->fifosize > 1)
353                 ubrlcr |= UBRLCR_FIFOEN;
354
355         spin_lock_irqsave(&port->lock, flags);
356
357         /*
358          * Update the per-port timeout.
359          */
360         uart_update_timeout(port, termios->c_cflag, baud);
361
362         port->read_status_mask = UARTDR_OVERR;
363         if (termios->c_iflag & INPCK)
364                 port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
365
366         /*
367          * Characters to ignore
368          */
369         port->ignore_status_mask = 0;
370         if (termios->c_iflag & IGNPAR)
371                 port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
372         if (termios->c_iflag & IGNBRK) {
373                 /*
374                  * If we're ignoring parity and break indicators,
375                  * ignore overruns to (for real raw support).
376                  */
377                 if (termios->c_iflag & IGNPAR)
378                         port->ignore_status_mask |= UARTDR_OVERR;
379         }
380
381         quot -= 1;
382
383         clps_writel(ubrlcr | quot, UBRLCR(port));
384
385         spin_unlock_irqrestore(&port->lock, flags);
386 }
387
388 static const char *clps711xuart_type(struct uart_port *port)
389 {
390         return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
391 }
392
393 /*
394  * Configure/autoconfigure the port.
395  */
396 static void clps711xuart_config_port(struct uart_port *port, int flags)
397 {
398         if (flags & UART_CONFIG_TYPE)
399                 port->type = PORT_CLPS711X;
400 }
401
402 static void clps711xuart_release_port(struct uart_port *port)
403 {
404 }
405
406 static int clps711xuart_request_port(struct uart_port *port)
407 {
408         return 0;
409 }
410
411 static struct uart_ops clps711x_pops = {
412         .tx_empty       = clps711xuart_tx_empty,
413         .set_mctrl      = clps711xuart_set_mctrl_null,
414         .get_mctrl      = clps711xuart_get_mctrl,
415         .stop_tx        = clps711xuart_stop_tx,
416         .start_tx       = clps711xuart_start_tx,
417         .stop_rx        = clps711xuart_stop_rx,
418         .enable_ms      = clps711xuart_enable_ms,
419         .break_ctl      = clps711xuart_break_ctl,
420         .startup        = clps711xuart_startup,
421         .shutdown       = clps711xuart_shutdown,
422         .set_termios    = clps711xuart_set_termios,
423         .type           = clps711xuart_type,
424         .config_port    = clps711xuart_config_port,
425         .release_port   = clps711xuart_release_port,
426         .request_port   = clps711xuart_request_port,
427 };
428
429 static struct uart_port clps711x_ports[UART_NR] = {
430         {
431                 .iobase         = SYSCON1,
432                 .irq            = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
433                 .uartclk        = 3686400,
434                 .fifosize       = 16,
435                 .ops            = &clps711x_pops,
436                 .line           = 0,
437                 .flags          = ASYNC_BOOT_AUTOCONF,
438         },
439         {
440                 .iobase         = SYSCON2,
441                 .irq            = IRQ_UTXINT2, /* IRQ_URXINT2 */
442                 .uartclk        = 3686400,
443                 .fifosize       = 16,
444                 .ops            = &clps711x_pops,
445                 .line           = 1,
446                 .flags          = ASYNC_BOOT_AUTOCONF,
447         }
448 };
449
450 #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
451 /*
452  *      Print a string to the serial port trying not to disturb
453  *      any possible real use of the port...
454  *
455  *      The console_lock must be held when we get here.
456  *
457  *      Note that this is called with interrupts already disabled
458  */
459 static void
460 clps711xuart_console_write(struct console *co, const char *s,
461                            unsigned int count)
462 {
463         struct uart_port *port = clps711x_ports + co->index;
464         unsigned int status, syscon;
465         int i;
466
467         /*
468          *      Ensure that the port is enabled.
469          */
470         syscon = clps_readl(SYSCON(port));
471         clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
472
473         /*
474          *      Now, do each character
475          */
476         for (i = 0; i < count; i++) {
477                 do {
478                         status = clps_readl(SYSFLG(port));
479                 } while (status & SYSFLG_UTXFF);
480                 clps_writel(s[i], UARTDR(port));
481                 if (s[i] == '\n') {
482                         do {
483                                 status = clps_readl(SYSFLG(port));
484                         } while (status & SYSFLG_UTXFF);
485                         clps_writel('\r', UARTDR(port));
486                 }
487         }
488
489         /*
490          *      Finally, wait for transmitter to become empty
491          *      and restore the uart state.
492          */
493         do {
494                 status = clps_readl(SYSFLG(port));
495         } while (status & SYSFLG_UBUSY);
496
497         clps_writel(syscon, SYSCON(port));
498 }
499
500 static void __init
501 clps711xuart_console_get_options(struct uart_port *port, int *baud,
502                                  int *parity, int *bits)
503 {
504         if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
505                 unsigned int ubrlcr, quot;
506
507                 ubrlcr = clps_readl(UBRLCR(port));
508
509                 *parity = 'n';
510                 if (ubrlcr & UBRLCR_PRTEN) {
511                         if (ubrlcr & UBRLCR_EVENPRT)
512                                 *parity = 'e';
513                         else
514                                 *parity = 'o';
515                 }
516
517                 if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
518                         *bits = 7;
519                 else
520                         *bits = 8;
521
522                 quot = ubrlcr & UBRLCR_BAUD_MASK;
523                 *baud = port->uartclk / (16 * (quot + 1));
524         }
525 }
526
527 static int __init clps711xuart_console_setup(struct console *co, char *options)
528 {
529         struct uart_port *port;
530         int baud = 38400;
531         int bits = 8;
532         int parity = 'n';
533         int flow = 'n';
534
535         /*
536          * Check whether an invalid uart number has been specified, and
537          * if so, search for the first available port that does have
538          * console support.
539          */
540         port = uart_get_console(clps711x_ports, UART_NR, co);
541
542         if (options)
543                 uart_parse_options(options, &baud, &parity, &bits, &flow);
544         else
545                 clps711xuart_console_get_options(port, &baud, &parity, &bits);
546
547         return uart_set_options(port, co, baud, parity, bits, flow);
548 }
549
550 extern struct uart_driver clps711x_reg;
551 static struct console clps711x_console = {
552         .name           = "ttyCL",
553         .write          = clps711xuart_console_write,
554         .device         = uart_console_device,
555         .setup          = clps711xuart_console_setup,
556         .flags          = CON_PRINTBUFFER,
557         .index          = -1,
558         .data           = &clps711x_reg,
559 };
560
561 static int __init clps711xuart_console_init(void)
562 {
563         register_console(&clps711x_console);
564         return 0;
565 }
566 console_initcall(clps711xuart_console_init);
567
568 #define CLPS711X_CONSOLE        &clps711x_console
569 #else
570 #define CLPS711X_CONSOLE        NULL
571 #endif
572
573 static struct uart_driver clps711x_reg = {
574         .driver_name            = "ttyCL",
575         .dev_name               = "ttyCL",
576         .major                  = SERIAL_CLPS711X_MAJOR,
577         .minor                  = SERIAL_CLPS711X_MINOR,
578         .nr                     = UART_NR,
579
580         .cons                   = CLPS711X_CONSOLE,
581 };
582
583 static int __init clps711xuart_init(void)
584 {
585         int ret, i;
586
587         printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.42 $\n");
588
589         ret = uart_register_driver(&clps711x_reg);
590         if (ret)
591                 return ret;
592
593         for (i = 0; i < UART_NR; i++)
594                 uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
595
596         return 0;
597 }
598
599 static void __exit clps711xuart_exit(void)
600 {
601         int i;
602
603         for (i = 0; i < UART_NR; i++)
604                 uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
605
606         uart_unregister_driver(&clps711x_reg);
607 }
608
609 module_init(clps711xuart_init);
610 module_exit(clps711xuart_exit);
611
612 MODULE_AUTHOR("Deep Blue Solutions Ltd");
613 MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.42 $");
614 MODULE_LICENSE("GPL");
615 MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);