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