~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/tty/serial/clps711x.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

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