Merge branch 'msm-uart' into for-next
[firefly-linux-kernel-4.4.55.git] / drivers / tty / serial / msm_serial.c
index 8e43a7b69e646accf723a020ba8ef5f8a05841d0..bfee9b4c66611045132893e1efcb6e8a8a925714 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2007 Google, Inc.
  * Author: Robert Love <rlove@google.com>
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -31,6 +32,7 @@
 #include <linux/serial.h>
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/delay.h>
 
 #include "msm_serial.h"
 
@@ -38,9 +40,20 @@ struct msm_port {
        struct uart_port        uart;
        char                    name[16];
        struct clk              *clk;
+       struct clk              *pclk;
        unsigned int            imr;
+       unsigned int            *gsbi_base;
+       int                     is_uartdm;
+       unsigned int            old_snap_state;
 };
 
+static inline void wait_for_xmitr(struct uart_port *port, int bits)
+{
+       if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
+               while ((msm_read(port, UART_ISR) & bits) != bits)
+                       cpu_relax();
+}
+
 static void msm_stop_tx(struct uart_port *port)
 {
        struct msm_port *msm_port = UART_TO_MSM(port);
@@ -73,6 +86,61 @@ static void msm_enable_ms(struct uart_port *port)
        msm_write(port, msm_port->imr, UART_IMR);
 }
 
+static void handle_rx_dm(struct uart_port *port, unsigned int misr)
+{
+       struct tty_struct *tty = port->state->port.tty;
+       unsigned int sr;
+       int count = 0;
+       struct msm_port *msm_port = UART_TO_MSM(port);
+
+       if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
+               port->icount.overrun++;
+               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+       }
+
+       if (misr & UART_IMR_RXSTALE) {
+               count = msm_read(port, UARTDM_RX_TOTAL_SNAP) -
+                       msm_port->old_snap_state;
+               msm_port->old_snap_state = 0;
+       } else {
+               count = 4 * (msm_read(port, UART_RFWR));
+               msm_port->old_snap_state += count;
+       }
+
+       /* TODO: Precise error reporting */
+
+       port->icount.rx += count;
+
+       while (count > 0) {
+               unsigned int c;
+
+               sr = msm_read(port, UART_SR);
+               if ((sr & UART_SR_RX_READY) == 0) {
+                       msm_port->old_snap_state -= count;
+                       break;
+               }
+               c = msm_read(port, UARTDM_RF);
+               if (sr & UART_SR_RX_BREAK) {
+                       port->icount.brk++;
+                       if (uart_handle_break(port))
+                               continue;
+               } else if (sr & UART_SR_PAR_FRAME_ERR)
+                       port->icount.frame++;
+
+               /* TODO: handle sysrq */
+               tty_insert_flip_string(tty, (char *) &c,
+                                      (count > 4) ? 4 : count);
+               count -= 4;
+       }
+
+       tty_flip_buffer_push(tty);
+       if (misr & (UART_IMR_RXSTALE))
+               msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+       msm_write(port, 0xFFFFFF, UARTDM_DMRX);
+       msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+}
+
 static void handle_rx(struct uart_port *port)
 {
        struct tty_struct *tty = port->state->port.tty;
@@ -121,6 +189,12 @@ static void handle_rx(struct uart_port *port)
        tty_flip_buffer_push(tty);
 }
 
+static void reset_dm_count(struct uart_port *port)
+{
+       wait_for_xmitr(port, UART_ISR_TX_READY);
+       msm_write(port, 1, UARTDM_NCF_TX);
+}
+
 static void handle_tx(struct uart_port *port)
 {
        struct circ_buf *xmit = &port->state->xmit;
@@ -128,11 +202,18 @@ static void handle_tx(struct uart_port *port)
        int sent_tx;
 
        if (port->x_char) {
-               msm_write(port, port->x_char, UART_TF);
+               if (msm_port->is_uartdm)
+                       reset_dm_count(port);
+
+               msm_write(port, port->x_char,
+                         msm_port->is_uartdm ? UARTDM_TF : UART_TF);
                port->icount.tx++;
                port->x_char = 0;
        }
 
+       if (msm_port->is_uartdm)
+               reset_dm_count(port);
+
        while (msm_read(port, UART_SR) & UART_SR_TX_READY) {
                if (uart_circ_empty(xmit)) {
                        /* disable tx interrupts */
@@ -140,8 +221,11 @@ static void handle_tx(struct uart_port *port)
                        msm_write(port, msm_port->imr, UART_IMR);
                        break;
                }
+               msm_write(port, xmit->buf[xmit->tail],
+                         msm_port->is_uartdm ? UARTDM_TF : UART_TF);
 
-               msm_write(port, xmit->buf[xmit->tail], UART_TF);
+               if (msm_port->is_uartdm)
+                       reset_dm_count(port);
 
                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
                port->icount.tx++;
@@ -169,8 +253,12 @@ static irqreturn_t msm_irq(int irq, void *dev_id)
        misr = msm_read(port, UART_MISR);
        msm_write(port, 0, UART_IMR); /* disable interrupt */
 
-       if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE))
-               handle_rx(port);
+       if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) {
+               if (msm_port->is_uartdm)
+                       handle_rx_dm(port, misr);
+               else
+                       handle_rx(port);
+       }
        if (misr & UART_IMR_TXLEV)
                handle_tx(port);
        if (misr & UART_IMR_DELTA_CTS)
@@ -192,10 +280,21 @@ static unsigned int msm_get_mctrl(struct uart_port *port)
        return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS;
 }
 
-static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+
+static void msm_reset(struct uart_port *port)
 {
-       unsigned int mr;
+       /* reset everything */
+       msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
+       msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
+       msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+       msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
+       msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
+       msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
+}
 
+void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       unsigned int mr;
        mr = msm_read(port, UART_MR1);
 
        if (!(mctrl & TIOCM_RTS)) {
@@ -219,6 +318,7 @@ static void msm_break_ctl(struct uart_port *port, int break_ctl)
 static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
 {
        unsigned int baud_code, rxstale, watermark;
+       struct msm_port *msm_port = UART_TO_MSM(port);
 
        switch (baud) {
        case 300:
@@ -273,6 +373,9 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
                break;
        }
 
+       if (msm_port->is_uartdm)
+               msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
+
        msm_write(port, baud_code, UART_CSR);
 
        /* RX stale watermark */
@@ -288,25 +391,23 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
        /* set TX watermark */
        msm_write(port, 10, UART_TFWR);
 
+       if (msm_port->is_uartdm) {
+               msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+               msm_write(port, 0xFFFFFF, UARTDM_DMRX);
+               msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+       }
+
        return baud;
 }
 
-static void msm_reset(struct uart_port *port)
-{
-       /* reset everything */
-       msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
-       msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
-       msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
-       msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
-       msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
-       msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
-}
 
 static void msm_init_clock(struct uart_port *port)
 {
        struct msm_port *msm_port = UART_TO_MSM(port);
 
        clk_enable(msm_port->clk);
+       if (!IS_ERR(msm_port->pclk))
+               clk_enable(msm_port->pclk);
        msm_serial_set_mnd_regs(port);
 }
 
@@ -347,15 +448,31 @@ static int msm_startup(struct uart_port *port)
                msm_write(port, data, UART_IPR);
        }
 
-       msm_reset(port);
+       data = 0;
+       if (!port->cons || (port->cons && !(port->cons->flags & CON_ENABLED))) {
+               msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
+               msm_reset(port);
+               data = UART_CR_TX_ENABLE;
+       }
+
+       data |= UART_CR_RX_ENABLE;
+       msm_write(port, data, UART_CR); /* enable TX & RX */
 
-       msm_write(port, 0x05, UART_CR); /* enable TX & RX */
+       /* Make sure IPR is not 0 to start with*/
+       if (msm_port->is_uartdm)
+               msm_write(port, UART_IPR_STALE_LSB, UART_IPR);
 
        /* turn on RX and CTS interrupts */
        msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE |
                        UART_IMR_CURRENT_CTS;
-       msm_write(port, msm_port->imr, UART_IMR);
 
+       if (msm_port->is_uartdm) {
+               msm_write(port, 0xFFFFFF, UARTDM_DMRX);
+               msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+               msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+       }
+
+       msm_write(port, msm_port->imr, UART_IMR);
        return 0;
 }
 
@@ -384,7 +501,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
        baud = msm_set_baud_rate(port, baud);
        if (tty_termios_baud_rate(termios))
                tty_termios_encode_baud_rate(termios, baud, baud);
-       
+
        /* calculate parity */
        mr = msm_read(port, UART_MR2);
        mr &= ~UART_MR2_PARITY_MODE;
@@ -454,48 +571,105 @@ static const char *msm_type(struct uart_port *port)
 static void msm_release_port(struct uart_port *port)
 {
        struct platform_device *pdev = to_platform_device(port->dev);
-       struct resource *resource;
+       struct msm_port *msm_port = UART_TO_MSM(port);
+       struct resource *uart_resource;
+       struct resource *gsbi_resource;
        resource_size_t size;
 
-       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (unlikely(!resource))
+       uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (unlikely(!uart_resource))
                return;
-       size = resource->end - resource->start + 1;
+       size = resource_size(uart_resource);
 
        release_mem_region(port->mapbase, size);
        iounmap(port->membase);
        port->membase = NULL;
+
+       if (msm_port->gsbi_base) {
+               iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
+                         GSBI_CONTROL);
+
+               gsbi_resource = platform_get_resource_byname(pdev,
+                                                            IORESOURCE_MEM,
+                                                            "gsbi_resource");
+
+               if (unlikely(!gsbi_resource))
+                       return;
+
+               size = resource_size(gsbi_resource);
+               release_mem_region(gsbi_resource->start, size);
+               iounmap(msm_port->gsbi_base);
+               msm_port->gsbi_base = NULL;
+       }
 }
 
 static int msm_request_port(struct uart_port *port)
 {
+       struct msm_port *msm_port = UART_TO_MSM(port);
        struct platform_device *pdev = to_platform_device(port->dev);
-       struct resource *resource;
+       struct resource *uart_resource;
+       struct resource *gsbi_resource;
        resource_size_t size;
+       int ret;
 
-       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (unlikely(!resource))
+       uart_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                    "uart_resource");
+       if (unlikely(!uart_resource))
                return -ENXIO;
-       size = resource->end - resource->start + 1;
 
-       if (unlikely(!request_mem_region(port->mapbase, size, "msm_serial")))
+       size = resource_size(uart_resource);
+
+       if (!request_mem_region(port->mapbase, size, "msm_serial"))
                return -EBUSY;
 
        port->membase = ioremap(port->mapbase, size);
        if (!port->membase) {
-               release_mem_region(port->mapbase, size);
-               return -EBUSY;
+               ret = -EBUSY;
+               goto fail_release_port;
+       }
+
+       gsbi_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                    "gsbi_resource");
+       /* Is this a GSBI-based port? */
+       if (gsbi_resource) {
+               size = resource_size(gsbi_resource);
+
+               if (!request_mem_region(gsbi_resource->start, size,
+                                                "msm_serial")) {
+                       ret = -EBUSY;
+                       goto fail_release_port;
+               }
+
+               msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
+               if (!msm_port->gsbi_base) {
+                       ret = -EBUSY;
+                       goto fail_release_gsbi;
+               }
        }
 
        return 0;
+
+fail_release_gsbi:
+       release_mem_region(gsbi_resource->start, size);
+fail_release_port:
+       release_mem_region(port->mapbase, size);
+       return ret;
 }
 
 static void msm_config_port(struct uart_port *port, int flags)
 {
+       struct msm_port *msm_port = UART_TO_MSM(port);
+       int ret;
        if (flags & UART_CONFIG_TYPE) {
                port->type = PORT_MSM;
-               msm_request_port(port);
+               ret = msm_request_port(port);
+               if (ret)
+                       return;
        }
+
+       if (msm_port->is_uartdm)
+               iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base +
+                         GSBI_CONTROL);
 }
 
 static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
@@ -515,9 +689,13 @@ static void msm_power(struct uart_port *port, unsigned int state,
        switch (state) {
        case 0:
                clk_enable(msm_port->clk);
+               if (!IS_ERR(msm_port->pclk))
+                       clk_enable(msm_port->pclk);
                break;
        case 3:
                clk_disable(msm_port->clk);
+               if (!IS_ERR(msm_port->pclk))
+                       clk_disable(msm_port->pclk);
                break;
        default:
                printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
@@ -550,7 +728,7 @@ static struct msm_port msm_uart_ports[] = {
                        .iotype = UPIO_MEM,
                        .ops = &msm_uart_pops,
                        .flags = UPF_BOOT_AUTOCONF,
-                       .fifosize = 512,
+                       .fifosize = 64,
                        .line = 0,
                },
        },
@@ -559,7 +737,7 @@ static struct msm_port msm_uart_ports[] = {
                        .iotype = UPIO_MEM,
                        .ops = &msm_uart_pops,
                        .flags = UPF_BOOT_AUTOCONF,
-                       .fifosize = 512,
+                       .fifosize = 64,
                        .line = 1,
                },
        },
@@ -585,9 +763,14 @@ static inline struct uart_port *get_port_from_line(unsigned int line)
 
 static void msm_console_putchar(struct uart_port *port, int c)
 {
+       struct msm_port *msm_port = UART_TO_MSM(port);
+
+       if (msm_port->is_uartdm)
+               reset_dm_count(port);
+
        while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
                ;
-       msm_write(port, c, UART_TF);
+       msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
 }
 
 static void msm_console_write(struct console *co, const char *s,
@@ -609,12 +792,14 @@ static void msm_console_write(struct console *co, const char *s,
 static int __init msm_console_setup(struct console *co, char *options)
 {
        struct uart_port *port;
+       struct msm_port *msm_port;
        int baud, flow, bits, parity;
 
        if (unlikely(co->index >= UART_NR || co->index < 0))
                return -ENXIO;
 
        port = get_port_from_line(co->index);
+       msm_port = UART_TO_MSM(port);
 
        if (unlikely(!port->membase))
                return -ENXIO;
@@ -638,6 +823,11 @@ static int __init msm_console_setup(struct console *co, char *options)
 
        msm_reset(port);
 
+       if (msm_port->is_uartdm) {
+               msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
+               msm_write(port, UART_CR_TX_ENABLE, UART_CR);
+       }
+
        printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line);
 
        return uart_set_options(port, co, baud, parity, bits, flow);
@@ -685,14 +875,32 @@ static int __init msm_serial_probe(struct platform_device *pdev)
        port->dev = &pdev->dev;
        msm_port = UART_TO_MSM(port);
 
-       msm_port->clk = clk_get(&pdev->dev, "uart_clk");
-       if (IS_ERR(msm_port->clk))
-               return PTR_ERR(msm_port->clk);
+       if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "gsbi_resource"))
+               msm_port->is_uartdm = 1;
+       else
+               msm_port->is_uartdm = 0;
+
+       if (msm_port->is_uartdm) {
+               msm_port->clk = clk_get(&pdev->dev, "gsbi_uart_clk");
+               msm_port->pclk = clk_get(&pdev->dev, "gsbi_pclk");
+       } else {
+               msm_port->clk = clk_get(&pdev->dev, "uart_clk");
+               msm_port->pclk = ERR_PTR(-ENOENT);
+       }
+
+       if (unlikely(IS_ERR(msm_port->clk) || (IS_ERR(msm_port->pclk) &&
+                                              msm_port->is_uartdm)))
+                       return PTR_ERR(msm_port->clk);
+
+       if (msm_port->is_uartdm)
+               clk_set_rate(msm_port->clk, 7372800);
+
        port->uartclk = clk_get_rate(msm_port->clk);
        printk(KERN_INFO "uartclk = %d\n", port->uartclk);
 
 
-       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                    "uart_resource");
        if (unlikely(!resource))
                return -ENXIO;
        port->mapbase = resource->start;