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