linux/drivers/tty/serial/21285.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/serial/21285.c
   3 *
   4 * Driver for the serial port on the 21285 StrongArm-110 core logic chip.
   5 *
   6 * Based on drivers/char/serial.c
   7 */
   8#include <linux/module.h>
   9#include <linux/tty.h>
  10#include <linux/ioport.h>
  11#include <linux/init.h>
  12#include <linux/console.h>
  13#include <linux/device.h>
  14#include <linux/tty_flip.h>
  15#include <linux/serial_core.h>
  16#include <linux/serial.h>
  17#include <linux/io.h>
  18
  19#include <asm/irq.h>
  20#include <asm/mach-types.h>
  21#include <asm/hardware/dec21285.h>
  22#include <mach/hardware.h>
  23
  24#define BAUD_BASE               (mem_fclk_21285/64)
  25
  26#define SERIAL_21285_NAME       "ttyFB"
  27#define SERIAL_21285_MAJOR      204
  28#define SERIAL_21285_MINOR      4
  29
  30#define RXSTAT_DUMMY_READ       0x80000000
  31#define RXSTAT_FRAME            (1 << 0)
  32#define RXSTAT_PARITY           (1 << 1)
  33#define RXSTAT_OVERRUN          (1 << 2)
  34#define RXSTAT_ANYERR           (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN)
  35
  36#define H_UBRLCR_BREAK          (1 << 0)
  37#define H_UBRLCR_PARENB         (1 << 1)
  38#define H_UBRLCR_PAREVN         (1 << 2)
  39#define H_UBRLCR_STOPB          (1 << 3)
  40#define H_UBRLCR_FIFO           (1 << 4)
  41
  42static const char serial21285_name[] = "Footbridge UART";
  43
  44#define tx_enabled(port)        ((port)->unused[0])
  45#define rx_enabled(port)        ((port)->unused[1])
  46
  47/*
  48 * The documented expression for selecting the divisor is:
  49 *  BAUD_BASE / baud - 1
  50 * However, typically BAUD_BASE is not divisible by baud, so
  51 * we want to select the divisor that gives us the minimum
  52 * error.  Therefore, we want:
  53 *  int(BAUD_BASE / baud - 0.5) ->
  54 *  int(BAUD_BASE / baud - (baud >> 1) / baud) ->
  55 *  int((BAUD_BASE - (baud >> 1)) / baud)
  56 */
  57
  58static void serial21285_stop_tx(struct uart_port *port)
  59{
  60        if (tx_enabled(port)) {
  61                disable_irq_nosync(IRQ_CONTX);
  62                tx_enabled(port) = 0;
  63        }
  64}
  65
  66static void serial21285_start_tx(struct uart_port *port)
  67{
  68        if (!tx_enabled(port)) {
  69                enable_irq(IRQ_CONTX);
  70                tx_enabled(port) = 1;
  71        }
  72}
  73
  74static void serial21285_stop_rx(struct uart_port *port)
  75{
  76        if (rx_enabled(port)) {
  77                disable_irq_nosync(IRQ_CONRX);
  78                rx_enabled(port) = 0;
  79        }
  80}
  81
  82static void serial21285_enable_ms(struct uart_port *port)
  83{
  84}
  85
  86static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)
  87{
  88        struct uart_port *port = dev_id;
  89        struct tty_struct *tty = port->state->port.tty;
  90        unsigned int status, ch, flag, rxs, max_count = 256;
  91
  92        status = *CSR_UARTFLG;
  93        while (!(status & 0x10) && max_count--) {
  94                ch = *CSR_UARTDR;
  95                flag = TTY_NORMAL;
  96                port->icount.rx++;
  97
  98                rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;
  99                if (unlikely(rxs & RXSTAT_ANYERR)) {
 100                        if (rxs & RXSTAT_PARITY)
 101                                port->icount.parity++;
 102                        else if (rxs & RXSTAT_FRAME)
 103                                port->icount.frame++;
 104                        if (rxs & RXSTAT_OVERRUN)
 105                                port->icount.overrun++;
 106
 107                        rxs &= port->read_status_mask;
 108
 109                        if (rxs & RXSTAT_PARITY)
 110                                flag = TTY_PARITY;
 111                        else if (rxs & RXSTAT_FRAME)
 112                                flag = TTY_FRAME;
 113                }
 114
 115                uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag);
 116
 117                status = *CSR_UARTFLG;
 118        }
 119        tty_flip_buffer_push(tty);
 120
 121        return IRQ_HANDLED;
 122}
 123
 124static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)
 125{
 126        struct uart_port *port = dev_id;
 127        struct circ_buf *xmit = &port->state->xmit;
 128        int count = 256;
 129
 130        if (port->x_char) {
 131                *CSR_UARTDR = port->x_char;
 132                port->icount.tx++;
 133                port->x_char = 0;
 134                goto out;
 135        }
 136        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
 137                serial21285_stop_tx(port);
 138                goto out;
 139        }
 140
 141        do {
 142                *CSR_UARTDR = xmit->buf[xmit->tail];
 143                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 144                port->icount.tx++;
 145                if (uart_circ_empty(xmit))
 146                        break;
 147        } while (--count > 0 && !(*CSR_UARTFLG & 0x20));
 148
 149        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 150                uart_write_wakeup(port);
 151
 152        if (uart_circ_empty(xmit))
 153                serial21285_stop_tx(port);
 154
 155 out:
 156        return IRQ_HANDLED;
 157}
 158
 159static unsigned int serial21285_tx_empty(struct uart_port *port)
 160{
 161        return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT;
 162}
 163
 164/* no modem control lines */
 165static unsigned int serial21285_get_mctrl(struct uart_port *port)
 166{
 167        return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
 168}
 169
 170static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl)
 171{
 172}
 173
 174static void serial21285_break_ctl(struct uart_port *port, int break_state)
 175{
 176        unsigned long flags;
 177        unsigned int h_lcr;
 178
 179        spin_lock_irqsave(&port->lock, flags);
 180        h_lcr = *CSR_H_UBRLCR;
 181        if (break_state)
 182                h_lcr |= H_UBRLCR_BREAK;
 183        else
 184                h_lcr &= ~H_UBRLCR_BREAK;
 185        *CSR_H_UBRLCR = h_lcr;
 186        spin_unlock_irqrestore(&port->lock, flags);
 187}
 188
 189static int serial21285_startup(struct uart_port *port)
 190{
 191        int ret;
 192
 193        tx_enabled(port) = 1;
 194        rx_enabled(port) = 1;
 195
 196        ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0,
 197                          serial21285_name, port);
 198        if (ret == 0) {
 199                ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0,
 200                                  serial21285_name, port);
 201                if (ret)
 202                        free_irq(IRQ_CONRX, port);
 203        }
 204
 205        return ret;
 206}
 207
 208static void serial21285_shutdown(struct uart_port *port)
 209{
 210        free_irq(IRQ_CONTX, port);
 211        free_irq(IRQ_CONRX, port);
 212}
 213
 214static void
 215serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
 216                        struct ktermios *old)
 217{
 218        unsigned long flags;
 219        unsigned int baud, quot, h_lcr, b;
 220
 221        /*
 222         * We don't support modem control lines.
 223         */
 224        termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
 225        termios->c_cflag |= CLOCAL;
 226
 227        /*
 228         * We don't support BREAK character recognition.
 229         */
 230        termios->c_iflag &= ~(IGNBRK | BRKINT);
 231
 232        /*
 233         * Ask the core to calculate the divisor for us.
 234         */
 235        baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
 236        quot = uart_get_divisor(port, baud);
 237        b = port->uartclk / (16 * quot);
 238        tty_termios_encode_baud_rate(termios, b, b);
 239
 240        switch (termios->c_cflag & CSIZE) {
 241        case CS5:
 242                h_lcr = 0x00;
 243                break;
 244        case CS6:
 245                h_lcr = 0x20;
 246                break;
 247        case CS7:
 248                h_lcr = 0x40;
 249                break;
 250        default: /* CS8 */
 251                h_lcr = 0x60;
 252                break;
 253        }
 254
 255        if (termios->c_cflag & CSTOPB)
 256                h_lcr |= H_UBRLCR_STOPB;
 257        if (termios->c_cflag & PARENB) {
 258                h_lcr |= H_UBRLCR_PARENB;
 259                if (!(termios->c_cflag & PARODD))
 260                        h_lcr |= H_UBRLCR_PAREVN;
 261        }
 262
 263        if (port->fifosize)
 264                h_lcr |= H_UBRLCR_FIFO;
 265
 266        spin_lock_irqsave(&port->lock, flags);
 267
 268        /*
 269         * Update the per-port timeout.
 270         */
 271        uart_update_timeout(port, termios->c_cflag, baud);
 272
 273        /*
 274         * Which character status flags are we interested in?
 275         */
 276        port->read_status_mask = RXSTAT_OVERRUN;
 277        if (termios->c_iflag & INPCK)
 278                port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
 279
 280        /*
 281         * Which character status flags should we ignore?
 282         */
 283        port->ignore_status_mask = 0;
 284        if (termios->c_iflag & IGNPAR)
 285                port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
 286        if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
 287                port->ignore_status_mask |= RXSTAT_OVERRUN;
 288
 289        /*
 290         * Ignore all characters if CREAD is not set.
 291         */
 292        if ((termios->c_cflag & CREAD) == 0)
 293                port->ignore_status_mask |= RXSTAT_DUMMY_READ;
 294
 295        quot -= 1;
 296
 297        *CSR_UARTCON = 0;
 298        *CSR_L_UBRLCR = quot & 0xff;
 299        *CSR_M_UBRLCR = (quot >> 8) & 0x0f;
 300        *CSR_H_UBRLCR = h_lcr;
 301        *CSR_UARTCON = 1;
 302
 303        spin_unlock_irqrestore(&port->lock, flags);
 304}
 305
 306static const char *serial21285_type(struct uart_port *port)
 307{
 308        return port->type == PORT_21285 ? "DC21285" : NULL;
 309}
 310
 311static void serial21285_release_port(struct uart_port *port)
 312{
 313        release_mem_region(port->mapbase, 32);
 314}
 315
 316static int serial21285_request_port(struct uart_port *port)
 317{
 318        return request_mem_region(port->mapbase, 32, serial21285_name)
 319                         != NULL ? 0 : -EBUSY;
 320}
 321
 322static void serial21285_config_port(struct uart_port *port, int flags)
 323{
 324        if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0)
 325                port->type = PORT_21285;
 326}
 327
 328/*
 329 * verify the new serial_struct (for TIOCSSERIAL).
 330 */
 331static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser)
 332{
 333        int ret = 0;
 334        if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
 335                ret = -EINVAL;
 336        if (ser->irq != NO_IRQ)
 337                ret = -EINVAL;
 338        if (ser->baud_base != port->uartclk / 16)
 339                ret = -EINVAL;
 340        return ret;
 341}
 342
 343static struct uart_ops serial21285_ops = {
 344        .tx_empty       = serial21285_tx_empty,
 345        .get_mctrl      = serial21285_get_mctrl,
 346        .set_mctrl      = serial21285_set_mctrl,
 347        .stop_tx        = serial21285_stop_tx,
 348        .start_tx       = serial21285_start_tx,
 349        .stop_rx        = serial21285_stop_rx,
 350        .enable_ms      = serial21285_enable_ms,
 351        .break_ctl      = serial21285_break_ctl,
 352        .startup        = serial21285_startup,
 353        .shutdown       = serial21285_shutdown,
 354        .set_termios    = serial21285_set_termios,
 355        .type           = serial21285_type,
 356        .release_port   = serial21285_release_port,
 357        .request_port   = serial21285_request_port,
 358        .config_port    = serial21285_config_port,
 359        .verify_port    = serial21285_verify_port,
 360};
 361
 362static struct uart_port serial21285_port = {
 363        .mapbase        = 0x42000160,
 364        .iotype         = UPIO_MEM,
 365        .irq            = NO_IRQ,
 366        .fifosize       = 16,
 367        .ops            = &serial21285_ops,
 368        .flags          = UPF_BOOT_AUTOCONF,
 369};
 370
 371static void serial21285_setup_ports(void)
 372{
 373        serial21285_port.uartclk = mem_fclk_21285 / 4;
 374}
 375
 376#ifdef CONFIG_SERIAL_21285_CONSOLE
 377static void serial21285_console_putchar(struct uart_port *port, int ch)
 378{
 379        while (*CSR_UARTFLG & 0x20)
 380                barrier();
 381        *CSR_UARTDR = ch;
 382}
 383
 384static void
 385serial21285_console_write(struct console *co, const char *s,
 386                          unsigned int count)
 387{
 388        uart_console_write(&serial21285_port, s, count, serial21285_console_putchar);
 389}
 390
 391static void __init
 392serial21285_get_options(struct uart_port *port, int *baud,
 393                        int *parity, int *bits)
 394{
 395        if (*CSR_UARTCON == 1) {
 396                unsigned int tmp;
 397
 398                tmp = *CSR_H_UBRLCR;
 399                switch (tmp & 0x60) {
 400                case 0x00:
 401                        *bits = 5;
 402                        break;
 403                case 0x20:
 404                        *bits = 6;
 405                        break;
 406                case 0x40:
 407                        *bits = 7;
 408                        break;
 409                default:
 410                case 0x60:
 411                        *bits = 8;
 412                        break;
 413                }
 414
 415                if (tmp & H_UBRLCR_PARENB) {
 416                        *parity = 'o';
 417                        if (tmp & H_UBRLCR_PAREVN)
 418                                *parity = 'e';
 419                }
 420
 421                tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8);
 422
 423                *baud = port->uartclk / (16 * (tmp + 1));
 424        }
 425}
 426
 427static int __init serial21285_console_setup(struct console *co, char *options)
 428{
 429        struct uart_port *port = &serial21285_port;
 430        int baud = 9600;
 431        int bits = 8;
 432        int parity = 'n';
 433        int flow = 'n';
 434
 435        if (machine_is_personal_server())
 436                baud = 57600;
 437
 438        /*
 439         * Check whether an invalid uart number has been specified, and
 440         * if so, search for the first available port that does have
 441         * console support.
 442         */
 443        if (options)
 444                uart_parse_options(options, &baud, &parity, &bits, &flow);
 445        else
 446                serial21285_get_options(port, &baud, &parity, &bits);
 447
 448        return uart_set_options(port, co, baud, parity, bits, flow);
 449}
 450
 451static struct uart_driver serial21285_reg;
 452
 453static struct console serial21285_console =
 454{
 455        .name           = SERIAL_21285_NAME,
 456        .write          = serial21285_console_write,
 457        .device         = uart_console_device,
 458        .setup          = serial21285_console_setup,
 459        .flags          = CON_PRINTBUFFER,
 460        .index          = -1,
 461        .data           = &serial21285_reg,
 462};
 463
 464static int __init rs285_console_init(void)
 465{
 466        serial21285_setup_ports();
 467        register_console(&serial21285_console);
 468        return 0;
 469}
 470console_initcall(rs285_console_init);
 471
 472#define SERIAL_21285_CONSOLE    &serial21285_console
 473#else
 474#define SERIAL_21285_CONSOLE    NULL
 475#endif
 476
 477static struct uart_driver serial21285_reg = {
 478        .owner                  = THIS_MODULE,
 479        .driver_name            = "ttyFB",
 480        .dev_name               = "ttyFB",
 481        .major                  = SERIAL_21285_MAJOR,
 482        .minor                  = SERIAL_21285_MINOR,
 483        .nr                     = 1,
 484        .cons                   = SERIAL_21285_CONSOLE,
 485};
 486
 487static int __init serial21285_init(void)
 488{
 489        int ret;
 490
 491        printk(KERN_INFO "Serial: 21285 driver\n");
 492
 493        serial21285_setup_ports();
 494
 495        ret = uart_register_driver(&serial21285_reg);
 496        if (ret == 0)
 497                uart_add_one_port(&serial21285_reg, &serial21285_port);
 498
 499        return ret;
 500}
 501
 502static void __exit serial21285_exit(void)
 503{
 504        uart_remove_one_port(&serial21285_reg, &serial21285_port);
 505        uart_unregister_driver(&serial21285_reg);
 506}
 507
 508module_init(serial21285_init);
 509module_exit(serial21285_exit);
 510
 511MODULE_LICENSE("GPL");
 512MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver");
 513MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
 514