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