serial: liteuart: move polling putchar() function
[platform/kernel/linux-starfive.git] / drivers / tty / serial / liteuart.c
index 062812f..ef557d5 100644 (file)
@@ -5,7 +5,9 @@
  * Copyright (C) 2019-2020 Antmicro <www.antmicro.com>
  */
 
+#include <linux/bits.h>
 #include <linux/console.h>
+#include <linux/interrupt.h>
 #include <linux/litex.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #define OFF_EV_ENABLE  0x14
 
 /* events */
-#define EV_TX          0x1
-#define EV_RX          0x2
+#define EV_TX          BIT(0)
+#define EV_RX          BIT(1)
 
 struct liteuart_port {
        struct uart_port port;
        struct timer_list timer;
        u32 id;
+       u8 irq_reg;
 };
 
 #define to_liteuart_port(port) container_of(port, struct liteuart_port, port)
@@ -57,7 +60,7 @@ static struct console liteuart_console;
 
 static struct uart_driver liteuart_driver = {
        .owner = THIS_MODULE,
-       .driver_name = "liteuart",
+       .driver_name = KBUILD_MODNAME,
        .dev_name = "ttyLXU",
        .major = 0,
        .minor = 0,
@@ -67,38 +70,95 @@ static struct uart_driver liteuart_driver = {
 #endif
 };
 
-static void liteuart_timer(struct timer_list *t)
+static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask)
+{
+       struct liteuart_port *uart = to_liteuart_port(port);
+
+       if (set)
+               uart->irq_reg |= mask;
+       else
+               uart->irq_reg &= ~mask;
+
+       if (port->irq)
+               litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg);
+}
+
+static void liteuart_stop_tx(struct uart_port *port)
+{
+       liteuart_update_irq_reg(port, false, EV_TX);
+}
+
+static void liteuart_start_tx(struct uart_port *port)
+{
+       liteuart_update_irq_reg(port, true, EV_TX);
+}
+
+static void liteuart_stop_rx(struct uart_port *port)
+{
+       struct liteuart_port *uart = to_liteuart_port(port);
+
+       /* just delete timer */
+       del_timer(&uart->timer);
+}
+
+static void liteuart_rx_chars(struct uart_port *port)
 {
-       struct liteuart_port *uart = from_timer(uart, t, timer);
-       struct uart_port *port = &uart->port;
        unsigned char __iomem *membase = port->membase;
-       unsigned int flg = TTY_NORMAL;
-       int ch;
-       unsigned long status;
+       u8 ch;
 
-       while ((status = !litex_read8(membase + OFF_RXEMPTY)) == 1) {
+       while (!litex_read8(membase + OFF_RXEMPTY)) {
                ch = litex_read8(membase + OFF_RXTX);
                port->icount.rx++;
 
                /* necessary for RXEMPTY to refresh its value */
-               litex_write8(membase + OFF_EV_PENDING, EV_TX | EV_RX);
+               litex_write8(membase + OFF_EV_PENDING, EV_RX);
 
                /* no overflow bits in status */
                if (!(uart_handle_sysrq_char(port, ch)))
-                       uart_insert_char(port, status, 0, ch, flg);
-
-               tty_flip_buffer_push(&port->state->port);
+                       uart_insert_char(port, 1, 0, ch, TTY_NORMAL);
        }
 
-       mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
+       tty_flip_buffer_push(&port->state->port);
 }
 
-static void liteuart_putchar(struct uart_port *port, unsigned char ch)
+static void liteuart_tx_chars(struct uart_port *port)
 {
-       while (litex_read8(port->membase + OFF_TXFULL))
-               cpu_relax();
+       u8 ch;
 
-       litex_write8(port->membase + OFF_RXTX, ch);
+       uart_port_tx(port, ch,
+               !litex_read8(port->membase + OFF_TXFULL),
+               litex_write8(port->membase + OFF_RXTX, ch));
+}
+
+static irqreturn_t liteuart_interrupt(int irq, void *data)
+{
+       struct liteuart_port *uart = data;
+       struct uart_port *port = &uart->port;
+       unsigned long flags;
+       u8 isr;
+
+       /*
+        * if polling, the context would be "in_serving_softirq", so use
+        * irq[save|restore] spin_lock variants to cover all possibilities
+        */
+       spin_lock_irqsave(&port->lock, flags);
+       isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg;
+       if (isr & EV_RX)
+               liteuart_rx_chars(port);
+       if (isr & EV_TX)
+               liteuart_tx_chars(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       return IRQ_RETVAL(isr);
+}
+
+static void liteuart_timer(struct timer_list *t)
+{
+       struct liteuart_port *uart = from_timer(uart, t, timer);
+       struct uart_port *port = &uart->port;
+
+       liteuart_interrupt(0, port);
+       mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
 }
 
 static unsigned int liteuart_tx_empty(struct uart_port *port)
@@ -120,60 +180,49 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port)
        return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
 }
 
-static void liteuart_stop_tx(struct uart_port *port)
-{
-}
-
-static void liteuart_start_tx(struct uart_port *port)
+static int liteuart_startup(struct uart_port *port)
 {
-       struct circ_buf *xmit = &port->state->xmit;
-       unsigned char ch;
-
-       if (unlikely(port->x_char)) {
-               litex_write8(port->membase + OFF_RXTX, port->x_char);
-               port->icount.tx++;
-               port->x_char = 0;
-       } else if (!uart_circ_empty(xmit)) {
-               while (xmit->head != xmit->tail) {
-                       ch = xmit->buf[xmit->tail];
-                       uart_xmit_advance(port, 1);
-                       liteuart_putchar(port, ch);
+       struct liteuart_port *uart = to_liteuart_port(port);
+       unsigned long flags;
+       int ret;
+
+       if (port->irq) {
+               ret = request_irq(port->irq, liteuart_interrupt, 0,
+                                 KBUILD_MODNAME, uart);
+               if (ret) {
+                       dev_warn(port->dev,
+                               "line %d irq %d failed: switch to polling\n",
+                               port->line, port->irq);
+                       port->irq = 0;
                }
        }
 
-       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-               uart_write_wakeup(port);
-}
-
-static void liteuart_stop_rx(struct uart_port *port)
-{
-       struct liteuart_port *uart = to_liteuart_port(port);
+       spin_lock_irqsave(&port->lock, flags);
+       /* only enabling rx irqs during startup */
+       liteuart_update_irq_reg(port, true, EV_RX);
+       spin_unlock_irqrestore(&port->lock, flags);
 
-       /* just delete timer */
-       del_timer(&uart->timer);
-}
+       if (!port->irq) {
+               timer_setup(&uart->timer, liteuart_timer, 0);
+               mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
+       }
 
-static void liteuart_break_ctl(struct uart_port *port, int break_state)
-{
-       /* LiteUART doesn't support sending break signal */
+       return 0;
 }
 
-static int liteuart_startup(struct uart_port *port)
+static void liteuart_shutdown(struct uart_port *port)
 {
        struct liteuart_port *uart = to_liteuart_port(port);
+       unsigned long flags;
 
-       /* disable events */
-       litex_write8(port->membase + OFF_EV_ENABLE, 0);
-
-       /* prepare timer for polling */
-       timer_setup(&uart->timer, liteuart_timer, 0);
-       mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
-
-       return 0;
-}
+       spin_lock_irqsave(&port->lock, flags);
+       liteuart_update_irq_reg(port, false, EV_RX | EV_TX);
+       spin_unlock_irqrestore(&port->lock, flags);
 
-static void liteuart_shutdown(struct uart_port *port)
-{
+       if (port->irq)
+               free_irq(port->irq, port);
+       else
+               del_timer_sync(&uart->timer);
 }
 
 static void liteuart_set_termios(struct uart_port *port, struct ktermios *new,
@@ -196,15 +245,6 @@ static const char *liteuart_type(struct uart_port *port)
        return "liteuart";
 }
 
-static void liteuart_release_port(struct uart_port *port)
-{
-}
-
-static int liteuart_request_port(struct uart_port *port)
-{
-       return 0;
-}
-
 static void liteuart_config_port(struct uart_port *port, int flags)
 {
        /*
@@ -231,13 +271,10 @@ static const struct uart_ops liteuart_ops = {
        .stop_tx        = liteuart_stop_tx,
        .start_tx       = liteuart_start_tx,
        .stop_rx        = liteuart_stop_rx,
-       .break_ctl      = liteuart_break_ctl,
        .startup        = liteuart_startup,
        .shutdown       = liteuart_shutdown,
        .set_termios    = liteuart_set_termios,
        .type           = liteuart_type,
-       .release_port   = liteuart_release_port,
-       .request_port   = liteuart_request_port,
        .config_port    = liteuart_config_port,
        .verify_port    = liteuart_verify_port,
 };
@@ -274,14 +311,18 @@ static int liteuart_probe(struct platform_device *pdev)
                goto err_erase_id;
        }
 
+       ret = platform_get_irq_optional(pdev, 0);
+       if (ret < 0 && ret != -ENXIO)
+               return ret;
+       if (ret > 0)
+               port->irq = ret;
+
        /* values not from device tree */
        port->dev = &pdev->dev;
        port->iotype = UPIO_MEM;
        port->flags = UPF_BOOT_AUTOCONF;
        port->ops = &liteuart_ops;
-       port->regshift = 2;
        port->fifosize = 16;
-       port->iobase = 1;
        port->type = PORT_UNKNOWN;
        port->line = dev_id;
        spin_lock_init(&port->lock);
@@ -321,13 +362,21 @@ static struct platform_driver liteuart_platform_driver = {
        .probe = liteuart_probe,
        .remove = liteuart_remove,
        .driver = {
-               .name = "liteuart",
+               .name = KBUILD_MODNAME,
                .of_match_table = liteuart_of_match,
        },
 };
 
 #ifdef CONFIG_SERIAL_LITEUART_CONSOLE
 
+static void liteuart_putchar(struct uart_port *port, unsigned char ch)
+{
+       while (litex_read8(port->membase + OFF_TXFULL))
+               cpu_relax();
+
+       litex_write8(port->membase + OFF_RXTX, ch);
+}
+
 static void liteuart_console_write(struct console *co, const char *s,
        unsigned int count)
 {
@@ -367,7 +416,7 @@ static int liteuart_console_setup(struct console *co, char *options)
 }
 
 static struct console liteuart_console = {
-       .name = "liteuart",
+       .name = KBUILD_MODNAME,
        .write = liteuart_console_write,
        .device = uart_console_device,
        .setup = liteuart_console_setup,
@@ -415,12 +464,10 @@ static int __init liteuart_init(void)
                return res;
 
        res = platform_driver_register(&liteuart_platform_driver);
-       if (res) {
+       if (res)
                uart_unregister_driver(&liteuart_driver);
-               return res;
-       }
 
-       return 0;
+       return res;
 }
 
 static void __exit liteuart_exit(void)