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