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        tty_flip_buffer_push(&port->state->port);
 135}
 136
 137static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
 138{
 139        struct uart_port *port = &pp->port;
 140        struct circ_buf *xmit = &port->state->xmit;
 141        unsigned int pending, count;
 142
 143        if (port->x_char) {
 144                /* Send special char - probably flow control */
 145                writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG);
 146                port->x_char = 0;
 147                port->icount.tx++;
 148                return;
 149        }
 150
 151        pending = uart_circ_chars_pending(xmit);
 152        if (pending > 0) {
 153                count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
 154                                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >>
 155                        ALTERA_JTAGUART_CONTROL_WSPACE_OFF;
 156                if (count > pending)
 157                        count = pending;
 158                if (count > 0) {
 159                        pending -= count;
 160                        while (count--) {
 161                                writel(xmit->buf[xmit->tail],
 162                                       port->membase + ALTERA_JTAGUART_DATA_REG);
 163                                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 164                                port->icount.tx++;
 165                        }
 166                        if (pending < WAKEUP_CHARS)
 167                                uart_write_wakeup(port);
 168                }
 169        }
 170
 171        if (pending == 0) {
 172                pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
 173                writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 174        }
 175}
 176
 177static irqreturn_t altera_jtaguart_interrupt(int irq, void *data)
 178{
 179        struct uart_port *port = data;
 180        struct altera_jtaguart *pp =
 181            container_of(port, struct altera_jtaguart, port);
 182        unsigned int isr;
 183
 184        isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >>
 185               ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr;
 186
 187        spin_lock(&port->lock);
 188
 189        if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK)
 190                altera_jtaguart_rx_chars(pp);
 191        if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK)
 192                altera_jtaguart_tx_chars(pp);
 193
 194        spin_unlock(&port->lock);
 195
 196        return IRQ_RETVAL(isr);
 197}
 198
 199static void altera_jtaguart_config_port(struct uart_port *port, int flags)
 200{
 201        port->type = PORT_ALTERA_JTAGUART;
 202
 203        /* Clear mask, so no surprise interrupts. */
 204        writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 205}
 206
 207static int altera_jtaguart_startup(struct uart_port *port)
 208{
 209        struct altera_jtaguart *pp =
 210            container_of(port, struct altera_jtaguart, port);
 211        unsigned long flags;
 212        int ret;
 213
 214        ret = request_irq(port->irq, altera_jtaguart_interrupt, 0,
 215                        DRV_NAME, port);
 216        if (ret) {
 217                pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d "
 218                       "interrupt vector=%d\n", port->line, port->irq);
 219                return ret;
 220        }
 221
 222        spin_lock_irqsave(&port->lock, flags);
 223
 224        /* Enable RX interrupts now */
 225        pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK;
 226        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 227
 228        spin_unlock_irqrestore(&port->lock, flags);
 229
 230        return 0;
 231}
 232
 233static void altera_jtaguart_shutdown(struct uart_port *port)
 234{
 235        struct altera_jtaguart *pp =
 236            container_of(port, struct altera_jtaguart, port);
 237        unsigned long flags;
 238
 239        spin_lock_irqsave(&port->lock, flags);
 240
 241        /* Disable all interrupts now */
 242        pp->imr = 0;
 243        writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
 244
 245        spin_unlock_irqrestore(&port->lock, flags);
 246
 247        free_irq(port->irq, port);
 248}
 249
 250static const char *altera_jtaguart_type(struct uart_port *port)
 251{
 252        return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL;
 253}
 254
 255static int altera_jtaguart_request_port(struct uart_port *port)
 256{
 257        /* UARTs always present */
 258        return 0;
 259}
 260
 261static void altera_jtaguart_release_port(struct uart_port *port)
 262{
 263        /* Nothing to release... */
 264}
 265
 266static int altera_jtaguart_verify_port(struct uart_port *port,
 267                                       struct serial_struct *ser)
 268{
 269        if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART)
 270                return -EINVAL;
 271        return 0;
 272}
 273
 274/*
 275 *      Define the basic serial functions we support.
 276 */
 277static const struct uart_ops altera_jtaguart_ops = {
 278        .tx_empty       = altera_jtaguart_tx_empty,
 279        .get_mctrl      = altera_jtaguart_get_mctrl,
 280        .set_mctrl      = altera_jtaguart_set_mctrl,
 281        .start_tx       = altera_jtaguart_start_tx,
 282        .stop_tx        = altera_jtaguart_stop_tx,
 283        .stop_rx        = altera_jtaguart_stop_rx,
 284        .break_ctl      = altera_jtaguart_break_ctl,
 285        .startup        = altera_jtaguart_startup,
 286        .shutdown       = altera_jtaguart_shutdown,
 287        .set_termios    = altera_jtaguart_set_termios,
 288        .type           = altera_jtaguart_type,
 289        .request_port   = altera_jtaguart_request_port,
 290        .release_port   = altera_jtaguart_release_port,
 291        .config_port    = altera_jtaguart_config_port,
 292        .verify_port    = altera_jtaguart_verify_port,
 293};
 294
 295#define ALTERA_JTAGUART_MAXPORTS 1
 296static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS];
 297
 298#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE)
 299
 300#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS)
 301static void altera_jtaguart_console_putc(struct uart_port *port, int c)
 302{
 303        unsigned long status;
 304        unsigned long flags;
 305
 306        spin_lock_irqsave(&port->lock, flags);
 307        while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) &
 308                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
 309                if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) {
 310                        spin_unlock_irqrestore(&port->lock, flags);
 311                        return; /* no connection activity */
 312                }
 313                spin_unlock_irqrestore(&port->lock, flags);
 314                cpu_relax();
 315                spin_lock_irqsave(&port->lock, flags);
 316        }
 317        writel(c, port->membase + ALTERA_JTAGUART_DATA_REG);
 318        spin_unlock_irqrestore(&port->lock, flags);
 319}
 320#else
 321static void altera_jtaguart_console_putc(struct uart_port *port, int c)
 322{
 323        unsigned long flags;
 324
 325        spin_lock_irqsave(&port->lock, flags);
 326        while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
 327                ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
 328                spin_unlock_irqrestore(&port->lock, flags);
 329                cpu_relax();
 330                spin_lock_irqsave(&port->lock, flags);
 331        }
 332        writel(c, port->membase + ALTERA_JTAGUART_DATA_REG);
 333        spin_unlock_irqrestore(&port->lock, flags);
 334}
 335#endif
 336
 337static void altera_jtaguart_console_write(struct console *co, const char *s,
 338                                          unsigned int count)
 339{
 340        struct uart_port *port = &(altera_jtaguart_ports + co->index)->port;
 341
 342        uart_console_write(port, s, count, altera_jtaguart_console_putc);
 343}
 344
 345static int __init altera_jtaguart_console_setup(struct console *co,
 346                                                char *options)
 347{
 348        struct uart_port *port;
 349
 350        if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS)
 351                return -EINVAL;
 352        port = &altera_jtaguart_ports[co->index].port;
 353        if (port->membase == NULL)
 354                return -ENODEV;
 355        return 0;
 356}
 357
 358static struct uart_driver altera_jtaguart_driver;
 359
 360static struct console altera_jtaguart_console = {
 361        .name   = "ttyJ",
 362        .write  = altera_jtaguart_console_write,
 363        .device = uart_console_device,
 364        .setup  = altera_jtaguart_console_setup,
 365        .flags  = CON_PRINTBUFFER,
 366        .index  = -1,
 367        .data   = &altera_jtaguart_driver,
 368};
 369
 370static int __init altera_jtaguart_console_init(void)
 371{
 372        register_console(&altera_jtaguart_console);
 373        return 0;
 374}
 375
 376console_initcall(altera_jtaguart_console_init);
 377
 378#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console)
 379
 380static void altera_jtaguart_earlycon_write(struct console *co, const char *s,
 381                                           unsigned int count)
 382{
 383        struct earlycon_device *dev = co->data;
 384
 385        uart_console_write(&dev->port, s, count, altera_jtaguart_console_putc);
 386}
 387
 388static int __init altera_jtaguart_earlycon_setup(struct earlycon_device *dev,
 389                                                 const char *options)
 390{
 391        if (!dev->port.membase)
 392                return -ENODEV;
 393
 394        dev->con->write = altera_jtaguart_earlycon_write;
 395        return 0;
 396}
 397
 398OF_EARLYCON_DECLARE(juart, "altr,juart-1.0", altera_jtaguart_earlycon_setup);
 399
 400#else
 401
 402#define ALTERA_JTAGUART_CONSOLE NULL
 403
 404#endif /* CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE */
 405
 406static struct uart_driver altera_jtaguart_driver = {
 407        .owner          = THIS_MODULE,
 408        .driver_name    = "altera_jtaguart",
 409        .dev_name       = "ttyJ",
 410        .major          = ALTERA_JTAGUART_MAJOR,
 411        .minor          = ALTERA_JTAGUART_MINOR,
 412        .nr             = ALTERA_JTAGUART_MAXPORTS,
 413        .cons           = ALTERA_JTAGUART_CONSOLE,
 414};
 415
 416static int altera_jtaguart_probe(struct platform_device *pdev)
 417{
 418        struct altera_jtaguart_platform_uart *platp =
 419                        dev_get_platdata(&pdev->dev);
 420        struct uart_port *port;
 421        struct resource *res_irq, *res_mem;
 422        int i = pdev->id;
 423
 424        /* -1 emphasizes that the platform must have one port, no .N suffix */
 425        if (i == -1)
 426                i = 0;
 427
 428        if (i >= ALTERA_JTAGUART_MAXPORTS)
 429                return -EINVAL;
 430
 431        port = &altera_jtaguart_ports[i].port;
 432
 433        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 434        if (res_mem)
 435                port->mapbase = res_mem->start;
 436        else if (platp)
 437                port->mapbase = platp->mapbase;
 438        else
 439                return -ENODEV;
 440
 441        res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 442        if (res_irq)
 443                port->irq = res_irq->start;
 444        else if (platp)
 445                port->irq = platp->irq;
 446        else
 447                return -ENODEV;
 448
 449        port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE);
 450        if (!port->membase)
 451                return -ENOMEM;
 452
 453        port->line = i;
 454        port->type = PORT_ALTERA_JTAGUART;
 455        port->iotype = SERIAL_IO_MEM;
 456        port->ops = &altera_jtaguart_ops;
 457        port->flags = UPF_BOOT_AUTOCONF;
 458        port->dev = &pdev->dev;
 459
 460        uart_add_one_port(&altera_jtaguart_driver, port);
 461
 462        return 0;
 463}
 464
 465static int altera_jtaguart_remove(struct platform_device *pdev)
 466{
 467        struct uart_port *port;
 468        int i = pdev->id;
 469
 470        if (i == -1)
 471                i = 0;
 472
 473        port = &altera_jtaguart_ports[i].port;
 474        uart_remove_one_port(&altera_jtaguart_driver, port);
 475        iounmap(port->membase);
 476
 477        return 0;
 478}
 479
 480#ifdef CONFIG_OF
 481static const struct of_device_id altera_jtaguart_match[] = {
 482        { .compatible = "ALTR,juart-1.0", },
 483        { .compatible = "altr,juart-1.0", },
 484        {},
 485};
 486MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
 487#endif /* CONFIG_OF */
 488
 489static struct platform_driver altera_jtaguart_platform_driver = {
 490        .probe  = altera_jtaguart_probe,
 491        .remove = altera_jtaguart_remove,
 492        .driver = {
 493                .name           = DRV_NAME,
 494                .of_match_table = of_match_ptr(altera_jtaguart_match),
 495        },
 496};
 497
 498static int __init altera_jtaguart_init(void)
 499{
 500        int rc;
 501
 502        rc = uart_register_driver(&altera_jtaguart_driver);
 503        if (rc)
 504                return rc;
 505        rc = platform_driver_register(&altera_jtaguart_platform_driver);
 506        if (rc)
 507                uart_unregister_driver(&altera_jtaguart_driver);
 508        return rc;
 509}
 510
 511static void __exit altera_jtaguart_exit(void)
 512{
 513        platform_driver_unregister(&altera_jtaguart_platform_driver);
 514        uart_unregister_driver(&altera_jtaguart_driver);
 515}
 516
 517module_init(altera_jtaguart_init);
 518module_exit(altera_jtaguart_exit);
 519
 520MODULE_DESCRIPTION("Altera JTAG UART driver");
 521MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
 522MODULE_LICENSE("GPL");
 523MODULE_ALIAS("platform:" DRV_NAME);
 524