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