linux/drivers/tty/serial/clps711x.c
<<
>>
Prefs
   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
  15#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
  16#define SUPPORT_SYSRQ
  17#endif
  18
  19#include <linux/module.h>
  20#include <linux/device.h>
  21#include <linux/console.h>
  22#include <linux/serial_core.h>
  23#include <linux/serial.h>
  24#include <linux/clk.h>
  25#include <linux/io.h>
  26#include <linux/tty.h>
  27#include <linux/tty_flip.h>
  28#include <linux/ioport.h>
  29#include <linux/of.h>
  30#include <linux/platform_device.h>
  31#include <linux/regmap.h>
  32
  33#include <linux/mfd/syscon.h>
  34#include <linux/mfd/syscon/clps711x.h>
  35
  36#include "serial_mctrl_gpio.h"
  37
  38#define UART_CLPS711X_DEVNAME   "ttyCL"
  39#define UART_CLPS711X_NR        2
  40#define UART_CLPS711X_MAJOR     204
  41#define UART_CLPS711X_MINOR     40
  42
  43#define UARTDR_OFFSET           (0x00)
  44#define UBRLCR_OFFSET           (0x40)
  45
  46#define UARTDR_FRMERR           (1 << 8)
  47#define UARTDR_PARERR           (1 << 9)
  48#define UARTDR_OVERR            (1 << 10)
  49
  50#define UBRLCR_BAUD_MASK        ((1 << 12) - 1)
  51#define UBRLCR_BREAK            (1 << 12)
  52#define UBRLCR_PRTEN            (1 << 13)
  53#define UBRLCR_EVENPRT          (1 << 14)
  54#define UBRLCR_XSTOP            (1 << 15)
  55#define UBRLCR_FIFOEN           (1 << 16)
  56#define UBRLCR_WRDLEN5          (0 << 17)
  57#define UBRLCR_WRDLEN6          (1 << 17)
  58#define UBRLCR_WRDLEN7          (2 << 17)
  59#define UBRLCR_WRDLEN8          (3 << 17)
  60#define UBRLCR_WRDLEN_MASK      (3 << 17)
  61
  62struct clps711x_port {
  63        struct uart_port        port;
  64        unsigned int            tx_enabled;
  65        int                     rx_irq;
  66        struct regmap           *syscon;
  67        struct mctrl_gpios      *gpios;
  68};
  69
  70static struct uart_driver clps711x_uart = {
  71        .owner          = THIS_MODULE,
  72        .driver_name    = UART_CLPS711X_DEVNAME,
  73        .dev_name       = UART_CLPS711X_DEVNAME,
  74        .major          = UART_CLPS711X_MAJOR,
  75        .minor          = UART_CLPS711X_MINOR,
  76        .nr             = UART_CLPS711X_NR,
  77};
  78
  79static void uart_clps711x_stop_tx(struct uart_port *port)
  80{
  81        struct clps711x_port *s = dev_get_drvdata(port->dev);
  82
  83        if (s->tx_enabled) {
  84                disable_irq(port->irq);
  85                s->tx_enabled = 0;
  86        }
  87}
  88
  89static void uart_clps711x_start_tx(struct uart_port *port)
  90{
  91        struct clps711x_port *s = dev_get_drvdata(port->dev);
  92
  93        if (!s->tx_enabled) {
  94                s->tx_enabled = 1;
  95                enable_irq(port->irq);
  96        }
  97}
  98
  99static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)
 100{
 101        struct uart_port *port = dev_id;
 102        struct clps711x_port *s = dev_get_drvdata(port->dev);
 103        unsigned int status, flg;
 104        u16 ch;
 105
 106        for (;;) {
 107                u32 sysflg = 0;
 108
 109                regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
 110                if (sysflg & SYSFLG_URXFE)
 111                        break;
 112
 113                ch = readw(port->membase + UARTDR_OFFSET);
 114                status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);
 115                ch &= 0xff;
 116
 117                port->icount.rx++;
 118                flg = TTY_NORMAL;
 119
 120                if (unlikely(status)) {
 121                        if (status & UARTDR_PARERR)
 122                                port->icount.parity++;
 123                        else if (status & UARTDR_FRMERR)
 124                                port->icount.frame++;
 125                        else if (status & UARTDR_OVERR)
 126                                port->icount.overrun++;
 127
 128                        status &= port->read_status_mask;
 129
 130                        if (status & UARTDR_PARERR)
 131                                flg = TTY_PARITY;
 132                        else if (status & UARTDR_FRMERR)
 133                                flg = TTY_FRAME;
 134                        else if (status & UARTDR_OVERR)
 135                                flg = TTY_OVERRUN;
 136                }
 137
 138                if (uart_handle_sysrq_char(port, ch))
 139                        continue;
 140
 141                if (status & port->ignore_status_mask)
 142                        continue;
 143
 144                uart_insert_char(port, status, UARTDR_OVERR, ch, flg);
 145        }
 146
 147        tty_flip_buffer_push(&port->state->port);
 148
 149        return IRQ_HANDLED;
 150}
 151
 152static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
 153{
 154        struct uart_port *port = dev_id;
 155        struct clps711x_port *s = dev_get_drvdata(port->dev);
 156        struct circ_buf *xmit = &port->state->xmit;
 157
 158        if (port->x_char) {
 159                writew(port->x_char, port->membase + UARTDR_OFFSET);
 160                port->icount.tx++;
 161                port->x_char = 0;
 162                return IRQ_HANDLED;
 163        }
 164
 165        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
 166                if (s->tx_enabled) {
 167                        disable_irq_nosync(port->irq);
 168                        s->tx_enabled = 0;
 169                }
 170                return IRQ_HANDLED;
 171        }
 172
 173        while (!uart_circ_empty(xmit)) {
 174                u32 sysflg = 0;
 175
 176                writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET);
 177                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 178                port->icount.tx++;
 179
 180                regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
 181                if (sysflg & SYSFLG_UTXFF)
 182                        break;
 183        }
 184
 185        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 186                uart_write_wakeup(port);
 187
 188        return IRQ_HANDLED;
 189}
 190
 191static unsigned int uart_clps711x_tx_empty(struct uart_port *port)
 192{
 193        struct clps711x_port *s = dev_get_drvdata(port->dev);
 194        u32 sysflg = 0;
 195
 196        regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
 197
 198        return (sysflg & SYSFLG_UBUSY) ? 0 : TIOCSER_TEMT;
 199}
 200
 201static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)
 202{
 203        unsigned int result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
 204        struct clps711x_port *s = dev_get_drvdata(port->dev);
 205
 206        return mctrl_gpio_get(s->gpios, &result);
 207}
 208
 209static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)
 210{
 211        struct clps711x_port *s = dev_get_drvdata(port->dev);
 212
 213        mctrl_gpio_set(s->gpios, mctrl);
 214}
 215
 216static void uart_clps711x_break_ctl(struct uart_port *port, int break_state)
 217{
 218        unsigned int ubrlcr;
 219
 220        ubrlcr = readl(port->membase + UBRLCR_OFFSET);
 221        if (break_state)
 222                ubrlcr |= UBRLCR_BREAK;
 223        else
 224                ubrlcr &= ~UBRLCR_BREAK;
 225        writel(ubrlcr, port->membase + UBRLCR_OFFSET);
 226}
 227
 228static void uart_clps711x_set_ldisc(struct uart_port *port,
 229                                    struct ktermios *termios)
 230{
 231        if (!port->line) {
 232                struct clps711x_port *s = dev_get_drvdata(port->dev);
 233
 234                regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN,
 235                                   (termios->c_line == N_IRDA) ? SYSCON1_SIREN : 0);
 236        }
 237}
 238
 239static int uart_clps711x_startup(struct uart_port *port)
 240{
 241        struct clps711x_port *s = dev_get_drvdata(port->dev);
 242
 243        /* Disable break */
 244        writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK,
 245               port->membase + UBRLCR_OFFSET);
 246
 247        /* Enable the port */
 248        return regmap_update_bits(s->syscon, SYSCON_OFFSET,
 249                                  SYSCON_UARTEN, SYSCON_UARTEN);
 250}
 251
 252static void uart_clps711x_shutdown(struct uart_port *port)
 253{
 254        struct clps711x_port *s = dev_get_drvdata(port->dev);
 255
 256        /* Disable the port */
 257        regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
 258}
 259
 260static void uart_clps711x_set_termios(struct uart_port *port,
 261                                      struct ktermios *termios,
 262                                      struct ktermios *old)
 263{
 264        u32 ubrlcr;
 265        unsigned int baud, quot;
 266
 267        /* Mask termios capabilities we don't support */
 268        termios->c_cflag &= ~CMSPAR;
 269        termios->c_iflag &= ~(BRKINT | IGNBRK);
 270
 271        /* Ask the core to calculate the divisor for us */
 272        baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096,
 273                                                      port->uartclk / 16);
 274        quot = uart_get_divisor(port, baud);
 275
 276        switch (termios->c_cflag & CSIZE) {
 277        case CS5:
 278                ubrlcr = UBRLCR_WRDLEN5;
 279                break;
 280        case CS6:
 281                ubrlcr = UBRLCR_WRDLEN6;
 282                break;
 283        case CS7:
 284                ubrlcr = UBRLCR_WRDLEN7;
 285                break;
 286        case CS8:
 287        default:
 288                ubrlcr = UBRLCR_WRDLEN8;
 289                break;
 290        }
 291
 292        if (termios->c_cflag & CSTOPB)
 293                ubrlcr |= UBRLCR_XSTOP;
 294
 295        if (termios->c_cflag & PARENB) {
 296                ubrlcr |= UBRLCR_PRTEN;
 297                if (!(termios->c_cflag & PARODD))
 298                        ubrlcr |= UBRLCR_EVENPRT;
 299        }
 300
 301        /* Enable FIFO */
 302        ubrlcr |= UBRLCR_FIFOEN;
 303
 304        /* Set read status mask */
 305        port->read_status_mask = UARTDR_OVERR;
 306        if (termios->c_iflag & INPCK)
 307                port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
 308
 309        /* Set status ignore mask */
 310        port->ignore_status_mask = 0;
 311        if (!(termios->c_cflag & CREAD))
 312                port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR |
 313                                            UARTDR_FRMERR;
 314
 315        uart_update_timeout(port, termios->c_cflag, baud);
 316
 317        writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET);
 318}
 319
 320static const char *uart_clps711x_type(struct uart_port *port)
 321{
 322        return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL;
 323}
 324
 325static void uart_clps711x_config_port(struct uart_port *port, int flags)
 326{
 327        if (flags & UART_CONFIG_TYPE)
 328                port->type = PORT_CLPS711X;
 329}
 330
 331static void uart_clps711x_nop_void(struct uart_port *port)
 332{
 333}
 334
 335static int uart_clps711x_nop_int(struct uart_port *port)
 336{
 337        return 0;
 338}
 339
 340static const struct uart_ops uart_clps711x_ops = {
 341        .tx_empty       = uart_clps711x_tx_empty,
 342        .set_mctrl      = uart_clps711x_set_mctrl,
 343        .get_mctrl      = uart_clps711x_get_mctrl,
 344        .stop_tx        = uart_clps711x_stop_tx,
 345        .start_tx       = uart_clps711x_start_tx,
 346        .stop_rx        = uart_clps711x_nop_void,
 347        .break_ctl      = uart_clps711x_break_ctl,
 348        .set_ldisc      = uart_clps711x_set_ldisc,
 349        .startup        = uart_clps711x_startup,
 350        .shutdown       = uart_clps711x_shutdown,
 351        .set_termios    = uart_clps711x_set_termios,
 352        .type           = uart_clps711x_type,
 353        .config_port    = uart_clps711x_config_port,
 354        .release_port   = uart_clps711x_nop_void,
 355        .request_port   = uart_clps711x_nop_int,
 356};
 357
 358#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
 359static void uart_clps711x_console_putchar(struct uart_port *port, int ch)
 360{
 361        struct clps711x_port *s = dev_get_drvdata(port->dev);
 362        u32 sysflg = 0;
 363
 364        /* Wait for FIFO is not full */
 365        do {
 366                regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
 367        } while (sysflg & SYSFLG_UTXFF);
 368
 369        writew(ch, port->membase + UARTDR_OFFSET);
 370}
 371
 372static void uart_clps711x_console_write(struct console *co, const char *c,
 373                                        unsigned n)
 374{
 375        struct uart_port *port = clps711x_uart.state[co->index].uart_port;
 376        struct clps711x_port *s = dev_get_drvdata(port->dev);
 377        u32 sysflg = 0;
 378
 379        uart_console_write(port, c, n, uart_clps711x_console_putchar);
 380
 381        /* Wait for transmitter to become empty */
 382        do {
 383                regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
 384        } while (sysflg & SYSFLG_UBUSY);
 385}
 386
 387static int uart_clps711x_console_setup(struct console *co, char *options)
 388{
 389        int baud = 38400, bits = 8, parity = 'n', flow = 'n';
 390        int ret, index = co->index;
 391        struct clps711x_port *s;
 392        struct uart_port *port;
 393        unsigned int quot;
 394        u32 ubrlcr;
 395
 396        if (index < 0 || index >= UART_CLPS711X_NR)
 397                return -EINVAL;
 398
 399        port = clps711x_uart.state[index].uart_port;
 400        if (!port)
 401                return -ENODEV;
 402
 403        s = dev_get_drvdata(port->dev);
 404
 405        if (!options) {
 406                u32 syscon = 0;
 407
 408                regmap_read(s->syscon, SYSCON_OFFSET, &syscon);
 409                if (syscon & SYSCON_UARTEN) {
 410                        ubrlcr = readl(port->membase + UBRLCR_OFFSET);
 411
 412                        if (ubrlcr & UBRLCR_PRTEN) {
 413                                if (ubrlcr & UBRLCR_EVENPRT)
 414                                        parity = 'e';
 415                                else
 416                                        parity = 'o';
 417                        }
 418
 419                        if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
 420                                bits = 7;
 421
 422                        quot = ubrlcr & UBRLCR_BAUD_MASK;
 423                        baud = port->uartclk / (16 * (quot + 1));
 424                }
 425        } else
 426                uart_parse_options(options, &baud, &parity, &bits, &flow);
 427
 428        ret = uart_set_options(port, co, baud, parity, bits, flow);
 429        if (ret)
 430                return ret;
 431
 432        return regmap_update_bits(s->syscon, SYSCON_OFFSET,
 433                                  SYSCON_UARTEN, SYSCON_UARTEN);
 434}
 435
 436static struct console clps711x_console = {
 437        .name   = UART_CLPS711X_DEVNAME,
 438        .device = uart_console_device,
 439        .write  = uart_clps711x_console_write,
 440        .setup  = uart_clps711x_console_setup,
 441        .flags  = CON_PRINTBUFFER,
 442        .index  = -1,
 443};
 444#endif
 445
 446static int uart_clps711x_probe(struct platform_device *pdev)
 447{
 448        struct device_node *np = pdev->dev.of_node;
 449        int ret, index = np ? of_alias_get_id(np, "serial") : pdev->id;
 450        struct clps711x_port *s;
 451        struct resource *res;
 452        struct clk *uart_clk;
 453        int irq;
 454
 455        if (index < 0 || index >= UART_CLPS711X_NR)
 456                return -EINVAL;
 457
 458        s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
 459        if (!s)
 460                return -ENOMEM;
 461
 462        uart_clk = devm_clk_get(&pdev->dev, NULL);
 463        if (IS_ERR(uart_clk))
 464                return PTR_ERR(uart_clk);
 465
 466        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 467        s->port.membase = devm_ioremap_resource(&pdev->dev, res);
 468        if (IS_ERR(s->port.membase))
 469                return PTR_ERR(s->port.membase);
 470
 471        irq = platform_get_irq(pdev, 0);
 472        if (irq < 0)
 473                return irq;
 474        s->port.irq = irq;
 475
 476        s->rx_irq = platform_get_irq(pdev, 1);
 477        if (s->rx_irq < 0)
 478                return s->rx_irq;
 479
 480        if (!np) {
 481                char syscon_name[9];
 482
 483                sprintf(syscon_name, "syscon.%i", index + 1);
 484                s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name);
 485                if (IS_ERR(s->syscon))
 486                        return PTR_ERR(s->syscon);
 487        } else {
 488                s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
 489                if (IS_ERR(s->syscon))
 490                        return PTR_ERR(s->syscon);
 491        }
 492
 493        s->port.line            = index;
 494        s->port.dev             = &pdev->dev;
 495        s->port.iotype          = UPIO_MEM32;
 496        s->port.mapbase         = res->start;
 497        s->port.type            = PORT_CLPS711X;
 498        s->port.fifosize        = 16;
 499        s->port.flags           = UPF_SKIP_TEST | UPF_FIXED_TYPE;
 500        s->port.uartclk         = clk_get_rate(uart_clk);
 501        s->port.ops             = &uart_clps711x_ops;
 502
 503        platform_set_drvdata(pdev, s);
 504
 505        s->gpios = mctrl_gpio_init_noauto(&pdev->dev, 0);
 506        if (IS_ERR(s->gpios))
 507            return PTR_ERR(s->gpios);
 508
 509        ret = uart_add_one_port(&clps711x_uart, &s->port);
 510        if (ret)
 511                return ret;
 512
 513        /* Disable port */
 514        if (!uart_console(&s->port))
 515                regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
 516
 517        s->tx_enabled = 1;
 518
 519        ret = devm_request_irq(&pdev->dev, s->port.irq, uart_clps711x_int_tx, 0,
 520                               dev_name(&pdev->dev), &s->port);
 521        if (ret) {
 522                uart_remove_one_port(&clps711x_uart, &s->port);
 523                return ret;
 524        }
 525
 526        ret = devm_request_irq(&pdev->dev, s->rx_irq, uart_clps711x_int_rx, 0,
 527                               dev_name(&pdev->dev), &s->port);
 528        if (ret)
 529                uart_remove_one_port(&clps711x_uart, &s->port);
 530
 531        return ret;
 532}
 533
 534static int uart_clps711x_remove(struct platform_device *pdev)
 535{
 536        struct clps711x_port *s = platform_get_drvdata(pdev);
 537
 538        return uart_remove_one_port(&clps711x_uart, &s->port);
 539}
 540
 541static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {
 542        { .compatible = "cirrus,ep7209-uart", },
 543        { }
 544};
 545MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids);
 546
 547static struct platform_driver clps711x_uart_platform = {
 548        .driver = {
 549                .name           = "clps711x-uart",
 550                .of_match_table = of_match_ptr(clps711x_uart_dt_ids),
 551        },
 552        .probe  = uart_clps711x_probe,
 553        .remove = uart_clps711x_remove,
 554};
 555
 556static int __init uart_clps711x_init(void)
 557{
 558        int ret;
 559
 560#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
 561        clps711x_uart.cons = &clps711x_console;
 562        clps711x_console.data = &clps711x_uart;
 563#endif
 564
 565        ret = uart_register_driver(&clps711x_uart);
 566        if (ret)
 567                return ret;
 568
 569        return platform_driver_register(&clps711x_uart_platform);
 570}
 571module_init(uart_clps711x_init);
 572
 573static void __exit uart_clps711x_exit(void)
 574{
 575        platform_driver_unregister(&clps711x_uart_platform);
 576        uart_unregister_driver(&clps711x_uart);
 577}
 578module_exit(uart_clps711x_exit);
 579
 580MODULE_AUTHOR("Deep Blue Solutions Ltd");
 581MODULE_DESCRIPTION("CLPS711X serial driver");
 582MODULE_LICENSE("GPL");
 583