linux/drivers/tty/serial/altera_jtaguart.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * altera_jtaguart.c -- Altera JTAG UART driver
   4 *
   5 * Based on mcf.c -- Freescale ColdFire UART driver
   6 *
   7 * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
   8 * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw>
   9 * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch>
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/init.h>
  14#include <linux/interrupt.h>
  15#include <linux/module.h>
  16#include <linux/console.h>
  17#include <linux/of.h>
  18#include <linux/tty.h>
  19#include <linux/tty_flip.h>
  20#include <linux/serial.h>
  21#include <linux/serial_core.h>
  22#include <linux/platform_device.h>
  23#include <linux/io.h>
  24#include <linux/altera_jtaguart.h>
  25
  26#define DRV_NAME "altera_jtaguart"
  27
  28/*
  29 * Altera JTAG UART register definitions according to the Altera JTAG UART
  30 * datasheet: https://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf
  31 */
  32
  33#define ALTERA_JTAGUART_SIZE                    8
  34
  35#define ALTERA_JTAGUART_DATA_REG                0
  36
  37#define ALTERA_JTAGUART_DATA_DATA_MSK           0x000000FF
  38#define ALTERA_JTAGUART_DATA_RVALID_MSK         0x00008000
  39#define ALTERA_JTAGUART_DATA_RAVAIL_MSK         0xFFFF0000
  40#define ALTERA_JTAGUART_DATA_RAVAIL_OFF         16
  41
  42#define ALTERA_JTAGUART_CONTROL_REG             4
  43
  44#define ALTERA_JTAGUART_CONTROL_RE_MSK          0x00000001
  45#define ALTERA_JTAGUART_CONTROL_WE_MSK          0x00000002
  46#define ALTERA_JTAGUART_CONTROL_RI_MSK          0x00000100
  47#define ALTERA_JTAGUART_CONTROL_RI_OFF          8
  48#define ALTERA_JTAGUART_CONTROL_WI_MSK          0x00000200
  49#define ALTERA_JTAGUART_CONTROL_AC_MSK          0x00000400
  50#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK      0xFFFF0000
  51#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF      16
  52
  53/*
  54 * Local per-uart structure.
  55 */
  56struct altera_jtaguart {
  57        struct uart_port port;
  58        unsigned int sigs;      /* Local copy of line sigs */
  59        unsigned long imr;      /* Local IMR mirror */
  60};
  61
  62static unsigned int altera_jtaguart_tx_empty(struct uart_port *port)
  63{
  64        return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
  65                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0;
  66}
  67
  68static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port)
  69{
  70        return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
  71}
  72
  73static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs)
  74{
  75}
  76
  77static void altera_jtaguart_start_tx(struct uart_port *port)
  78{
  79        struct altera_jtaguart *pp =
  80            container_of(port, struct altera_jtaguart, port);
  81
  82        pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK;
  83        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
  84}
  85
  86static void altera_jtaguart_stop_tx(struct uart_port *port)
  87{
  88        struct altera_jtaguart *pp =
  89            container_of(port, struct altera_jtaguart, port);
  90
  91        pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
  92        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
  93}
  94
  95static void altera_jtaguart_stop_rx(struct uart_port *port)
  96{
  97        struct altera_jtaguart *pp =
  98            container_of(port, struct altera_jtaguart, port);
  99
 100        pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK;
 101        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 102}
 103
 104static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state)
 105{
 106}
 107
 108static void altera_jtaguart_set_termios(struct uart_port *port,
 109                                        struct ktermios *termios,
 110                                        struct ktermios *old)
 111{
 112        /* Just copy the old termios settings back */
 113        if (old)
 114                tty_termios_copy_hw(termios, old);
 115}
 116
 117static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp)
 118{
 119        struct uart_port *port = &pp->port;
 120        unsigned char ch, flag;
 121        unsigned long status;
 122
 123        while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) &
 124               ALTERA_JTAGUART_DATA_RVALID_MSK) {
 125                ch = status & ALTERA_JTAGUART_DATA_DATA_MSK;
 126                flag = TTY_NORMAL;
 127                port->icount.rx++;
 128
 129                if (uart_handle_sysrq_char(port, ch))
 130                        continue;
 131                uart_insert_char(port, 0, 0, ch, flag);
 132        }
 133
 134        spin_unlock(&port->lock);
 135        tty_flip_buffer_push(&port->state->port);
 136        spin_lock(&port->lock);
 137}
 138
 139static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
 140{
 141        struct uart_port *port = &pp->port;
 142        struct circ_buf *xmit = &port->state->xmit;
 143        unsigned int pending, count;
 144
 145        if (port->x_char) {
 146                /* Send special char - probably flow control */
 147                writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG);
 148                port->x_char = 0;
 149                port->icount.tx++;
 150                return;
 151        }
 152
 153        pending = uart_circ_chars_pending(xmit);
 154        if (pending > 0) {
 155                count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
 156                                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >>
 157                        ALTERA_JTAGUART_CONTROL_WSPACE_OFF;
 158                if (count > pending)
 159                        count = pending;
 160                if (count > 0) {
 161                        pending -= count;
 162                        while (count--) {
 163                                writel(xmit->buf[xmit->tail],
 164                                       port->membase + ALTERA_JTAGUART_DATA_REG);
 165                                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 166                                port->icount.tx++;
 167                        }
 168                        if (pending < WAKEUP_CHARS)
 169                                uart_write_wakeup(port);
 170                }
 171        }
 172
 173        if (pending == 0) {
 174                pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
 175                writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 176        }
 177}
 178
 179static irqreturn_t altera_jtaguart_interrupt(int irq, void *data)
 180{
 181        struct uart_port *port = data;
 182        struct altera_jtaguart *pp =
 183            container_of(port, struct altera_jtaguart, port);
 184        unsigned int isr;
 185
 186        isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >>
 187               ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr;
 188
 189        spin_lock(&port->lock);
 190
 191        if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK)
 192                altera_jtaguart_rx_chars(pp);
 193        if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK)
 194                altera_jtaguart_tx_chars(pp);
 195
 196        spin_unlock(&port->lock);
 197
 198        return IRQ_RETVAL(isr);
 199}
 200
 201static void altera_jtaguart_config_port(struct uart_port *port, int flags)
 202{
 203        port->type = PORT_ALTERA_JTAGUART;
 204
 205        /* Clear mask, so no surprise interrupts. */
 206        writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 207}
 208
 209static int altera_jtaguart_startup(struct uart_port *port)
 210{
 211        struct altera_jtaguart *pp =
 212            container_of(port, struct altera_jtaguart, port);
 213        unsigned long flags;
 214        int ret;
 215
 216        ret = request_irq(port->irq, altera_jtaguart_interrupt, 0,
 217                        DRV_NAME, port);
 218        if (ret) {
 219                pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d "
 220                       "interrupt vector=%d\n", port->line, port->irq);
 221                return ret;
 222        }
 223
 224        spin_lock_irqsave(&port->lock, flags);
 225
 226        /* Enable RX interrupts now */
 227        pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK;
 228        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 229
 230        spin_unlock_irqrestore(&port->lock, flags);
 231
 232        return 0;
 233}
 234
 235static void altera_jtaguart_shutdown(struct uart_port *port)
 236{
 237        struct altera_jtaguart *pp =
 238            container_of(port, struct altera_jtaguart, port);
 239        unsigned long flags;
 240
 241        spin_lock_irqsave(&port->lock, flags);
 242
 243        /* Disable all interrupts now */
 244        pp->imr = 0;
 245        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 246
 247        spin_unlock_irqrestore(&port->lock, flags);
 248
 249        free_irq(port->irq, port);
 250}
 251
 252static const char *altera_jtaguart_type(struct uart_port *port)
 253{
 254        return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL;
 255}
 256
 257static int altera_jtaguart_request_port(struct uart_port *port)
 258{
 259        /* UARTs always present */
 260        return 0;
 261}
 262
 263static void altera_jtaguart_release_port(struct uart_port *port)
 264{
 265        /* Nothing to release... */
 266}
 267
 268static int altera_jtaguart_verify_port(struct uart_port *port,
 269                                       struct serial_struct *ser)
 270{
 271        if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART)
 272                return -EINVAL;
 273        return 0;
 274}
 275
 276/*
 277 *      Define the basic serial functions we support.
 278 */
 279static const struct uart_ops altera_jtaguart_ops = {
 280        .tx_empty       = altera_jtaguart_tx_empty,
 281        .get_mctrl      = altera_jtaguart_get_mctrl,
 282        .set_mctrl      = altera_jtaguart_set_mctrl,
 283        .start_tx       = altera_jtaguart_start_tx,
 284        .stop_tx        = altera_jtaguart_stop_tx,
 285        .stop_rx        = altera_jtaguart_stop_rx,
 286        .break_ctl      = altera_jtaguart_break_ctl,
 287        .startup        = altera_jtaguart_startup,
 288        .shutdown       = altera_jtaguart_shutdown,
 289        .set_termios    = altera_jtaguart_set_termios,
 290        .type           = altera_jtaguart_type,
 291        .request_port   = altera_jtaguart_request_port,
 292        .release_port   = altera_jtaguart_release_port,
 293        .config_port    = altera_jtaguart_config_port,
 294        .verify_port    = altera_jtaguart_verify_port,
 295};
 296
 297#define ALTERA_JTAGUART_MAXPORTS 1
 298static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS];
 299
 300#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE)
 301
 302#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS)
 303static void altera_jtaguart_console_putc(struct uart_port *port, int c)
 304{
 305        unsigned long status;
 306        unsigned long flags;
 307
 308        spin_lock_irqsave(&port->lock, flags);
 309        while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) &
 310                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
 311                if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) {
 312                        spin_unlock_irqrestore(&port->lock, flags);
 313                        return; /* no connection activity */
 314                }
 315                spin_unlock_irqrestore(&port->lock, flags);
 316                cpu_relax();
 317                spin_lock_irqsave(&port->lock, flags);
 318        }
 319        writel(c, port->membase + ALTERA_JTAGUART_DATA_REG);
 320        spin_unlock_irqrestore(&port->lock, flags);
 321}
 322#else
 323static void altera_jtaguart_console_putc(struct uart_port *port, int c)
 324{
 325        unsigned long flags;
 326
 327        spin_lock_irqsave(&port->lock, flags);
 328        while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
 329                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
 330                spin_unlock_irqrestore(&port->lock, flags);
 331                cpu_relax();
 332                spin_lock_irqsave(&port->lock, flags);
 333        }
 334        writel(c, port->membase + ALTERA_JTAGUART_DATA_REG);
 335        spin_unlock_irqrestore(&port->lock, flags);
 336}
 337#endif
 338
 339static void altera_jtaguart_console_write(struct console *co, const char *s,
 340                                          unsigned int count)
 341{
 342        struct uart_port *port = &(altera_jtaguart_ports + co->index)->port;
 343
 344        uart_console_write(port, s, count, altera_jtaguart_console_putc);
 345}
 346
 347static int __init altera_jtaguart_console_setup(struct console *co,
 348                                                char *options)
 349{
 350        struct uart_port *port;
 351
 352        if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS)
 353                return -EINVAL;
 354        port = &altera_jtaguart_ports[co->index].port;
 355        if (port->membase == NULL)
 356                return -ENODEV;
 357        return 0;
 358}
 359
 360static struct uart_driver altera_jtaguart_driver;
 361
 362static struct console altera_jtaguart_console = {
 363        .name   = "ttyJ",
 364        .write  = altera_jtaguart_console_write,
 365        .device = uart_console_device,
 366        .setup  = altera_jtaguart_console_setup,
 367        .flags  = CON_PRINTBUFFER,
 368        .index  = -1,
 369        .data   = &altera_jtaguart_driver,
 370};
 371
 372static int __init altera_jtaguart_console_init(void)
 373{
 374        register_console(&altera_jtaguart_console);
 375        return 0;
 376}
 377
 378console_initcall(altera_jtaguart_console_init);
 379
 380#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console)
 381
 382static void altera_jtaguart_earlycon_write(struct console *co, const char *s,
 383                                           unsigned int count)
 384{
 385        struct earlycon_device *dev = co->data;
 386
 387        uart_console_write(&dev->port, s, count, altera_jtaguart_console_putc);
 388}
 389
 390static int __init altera_jtaguart_earlycon_setup(struct earlycon_device *dev,
 391                                                 const char *options)
 392{
 393        if (!dev->port.membase)
 394                return -ENODEV;
 395
 396        dev->con->write = altera_jtaguart_earlycon_write;
 397        return 0;
 398}
 399
 400OF_EARLYCON_DECLARE(juart, "altr,juart-1.0", altera_jtaguart_earlycon_setup);
 401
 402#else
 403
 404#define ALTERA_JTAGUART_CONSOLE NULL
 405
 406#endif /* CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE */
 407
 408static struct uart_driver altera_jtaguart_driver = {
 409        .owner          = THIS_MODULE,
 410        .driver_name    = "altera_jtaguart",
 411        .dev_name       = "ttyJ",
 412        .major          = ALTERA_JTAGUART_MAJOR,
 413        .minor          = ALTERA_JTAGUART_MINOR,
 414        .nr             = ALTERA_JTAGUART_MAXPORTS,
 415        .cons           = ALTERA_JTAGUART_CONSOLE,
 416};
 417
 418static int altera_jtaguart_probe(struct platform_device *pdev)
 419{
 420        struct altera_jtaguart_platform_uart *platp =
 421                        dev_get_platdata(&pdev->dev);
 422        struct uart_port *port;
 423        struct resource *res_irq, *res_mem;
 424        int i = pdev->id;
 425
 426        /* -1 emphasizes that the platform must have one port, no .N suffix */
 427        if (i == -1)
 428                i = 0;
 429
 430        if (i >= ALTERA_JTAGUART_MAXPORTS)
 431                return -EINVAL;
 432
 433        port = &altera_jtaguart_ports[i].port;
 434
 435        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 436        if (res_mem)
 437                port->mapbase = res_mem->start;
 438        else if (platp)
 439                port->mapbase = platp->mapbase;
 440        else
 441                return -ENODEV;
 442
 443        res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 444        if (res_irq)
 445                port->irq = res_irq->start;
 446        else if (platp)
 447                port->irq = platp->irq;
 448        else
 449                return -ENODEV;
 450
 451        port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE);
 452        if (!port->membase)
 453                return -ENOMEM;
 454
 455        port->line = i;
 456        port->type = PORT_ALTERA_JTAGUART;
 457        port->iotype = SERIAL_IO_MEM;
 458        port->ops = &altera_jtaguart_ops;
 459        port->flags = UPF_BOOT_AUTOCONF;
 460        port->dev = &pdev->dev;
 461
 462        uart_add_one_port(&altera_jtaguart_driver, port);
 463
 464        return 0;
 465}
 466
 467static int altera_jtaguart_remove(struct platform_device *pdev)
 468{
 469        struct uart_port *port;
 470        int i = pdev->id;
 471
 472        if (i == -1)
 473                i = 0;
 474
 475        port = &altera_jtaguart_ports[i].port;
 476        uart_remove_one_port(&altera_jtaguart_driver, port);
 477        iounmap(port->membase);
 478
 479        return 0;
 480}
 481
 482#ifdef CONFIG_OF
 483static const struct of_device_id altera_jtaguart_match[] = {
 484        { .compatible = "ALTR,juart-1.0", },
 485        { .compatible = "altr,juart-1.0", },
 486        {},
 487};
 488MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
 489#endif /* CONFIG_OF */
 490
 491static struct platform_driver altera_jtaguart_platform_driver = {
 492        .probe  = altera_jtaguart_probe,
 493        .remove = altera_jtaguart_remove,
 494        .driver = {
 495                .name           = DRV_NAME,
 496                .of_match_table = of_match_ptr(altera_jtaguart_match),
 497        },
 498};
 499
 500static int __init altera_jtaguart_init(void)
 501{
 502        int rc;
 503
 504        rc = uart_register_driver(&altera_jtaguart_driver);
 505        if (rc)
 506                return rc;
 507        rc = platform_driver_register(&altera_jtaguart_platform_driver);
 508        if (rc)
 509                uart_unregister_driver(&altera_jtaguart_driver);
 510        return rc;
 511}
 512
 513static void __exit altera_jtaguart_exit(void)
 514{
 515        platform_driver_unregister(&altera_jtaguart_platform_driver);
 516        uart_unregister_driver(&altera_jtaguart_driver);
 517}
 518
 519module_init(altera_jtaguart_init);
 520module_exit(altera_jtaguart_exit);
 521
 522MODULE_DESCRIPTION("Altera JTAG UART driver");
 523MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
 524MODULE_LICENSE("GPL");
 525MODULE_ALIAS("platform:" DRV_NAME);
 526