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