Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[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 static void clps711xuart_console_putchar(struct uart_port *port, int ch)
428 {
429         while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
430                 barrier();
431         clps_writel(ch, UARTDR(port));
432 }
433
434 /*
435  *      Print a string to the serial port trying not to disturb
436  *      any possible real use of the port...
437  *
438  *      The console_lock must be held when we get here.
439  *
440  *      Note that this is called with interrupts already disabled
441  */
442 static void
443 clps711xuart_console_write(struct console *co, const char *s,
444                            unsigned int count)
445 {
446         struct uart_port *port = clps711x_ports + co->index;
447         unsigned int status, syscon;
448
449         /*
450          *      Ensure that the port is enabled.
451          */
452         syscon = clps_readl(SYSCON(port));
453         clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
454
455         uart_console_write(port, s, count, clps711xuart_console_putchar);
456
457         /*
458          *      Finally, wait for transmitter to become empty
459          *      and restore the uart state.
460          */
461         do {
462                 status = clps_readl(SYSFLG(port));
463         } while (status & SYSFLG_UBUSY);
464
465         clps_writel(syscon, SYSCON(port));
466 }
467
468 static void __init
469 clps711xuart_console_get_options(struct uart_port *port, int *baud,
470                                  int *parity, int *bits)
471 {
472         if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
473                 unsigned int ubrlcr, quot;
474
475                 ubrlcr = clps_readl(UBRLCR(port));
476
477                 *parity = 'n';
478                 if (ubrlcr & UBRLCR_PRTEN) {
479                         if (ubrlcr & UBRLCR_EVENPRT)
480                                 *parity = 'e';
481                         else
482                                 *parity = 'o';
483                 }
484
485                 if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
486                         *bits = 7;
487                 else
488                         *bits = 8;
489
490                 quot = ubrlcr & UBRLCR_BAUD_MASK;
491                 *baud = port->uartclk / (16 * (quot + 1));
492         }
493 }
494
495 static int __init clps711xuart_console_setup(struct console *co, char *options)
496 {
497         struct uart_port *port;
498         int baud = 38400;
499         int bits = 8;
500         int parity = 'n';
501         int flow = 'n';
502
503         /*
504          * Check whether an invalid uart number has been specified, and
505          * if so, search for the first available port that does have
506          * console support.
507          */
508         port = uart_get_console(clps711x_ports, UART_NR, co);
509
510         if (options)
511                 uart_parse_options(options, &baud, &parity, &bits, &flow);
512         else
513                 clps711xuart_console_get_options(port, &baud, &parity, &bits);
514
515         return uart_set_options(port, co, baud, parity, bits, flow);
516 }
517
518 static struct uart_driver clps711x_reg;
519 static struct console clps711x_console = {
520         .name           = "ttyCL",
521         .write          = clps711xuart_console_write,
522         .device         = uart_console_device,
523         .setup          = clps711xuart_console_setup,
524         .flags          = CON_PRINTBUFFER,
525         .index          = -1,
526         .data           = &clps711x_reg,
527 };
528
529 static int __init clps711xuart_console_init(void)
530 {
531         register_console(&clps711x_console);
532         return 0;
533 }
534 console_initcall(clps711xuart_console_init);
535
536 #define CLPS711X_CONSOLE        &clps711x_console
537 #else
538 #define CLPS711X_CONSOLE        NULL
539 #endif
540
541 static struct uart_driver clps711x_reg = {
542         .driver_name            = "ttyCL",
543         .dev_name               = "ttyCL",
544         .major                  = SERIAL_CLPS711X_MAJOR,
545         .minor                  = SERIAL_CLPS711X_MINOR,
546         .nr                     = UART_NR,
547
548         .cons                   = CLPS711X_CONSOLE,
549 };
550
551 static int __init clps711xuart_init(void)
552 {
553         int ret, i;
554
555         printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.42 $\n");
556
557         ret = uart_register_driver(&clps711x_reg);
558         if (ret)
559                 return ret;
560
561         for (i = 0; i < UART_NR; i++)
562                 uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
563
564         return 0;
565 }
566
567 static void __exit clps711xuart_exit(void)
568 {
569         int i;
570
571         for (i = 0; i < UART_NR; i++)
572                 uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
573
574         uart_unregister_driver(&clps711x_reg);
575 }
576
577 module_init(clps711xuart_init);
578 module_exit(clps711xuart_exit);
579
580 MODULE_AUTHOR("Deep Blue Solutions Ltd");
581 MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.42 $");
582 MODULE_LICENSE("GPL");
583 MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);