diff options
Diffstat (limited to 'drivers/tty/serial/msm_serial.c')
-rw-r--r-- | drivers/tty/serial/msm_serial.c | 286 |
1 files changed, 247 insertions, 39 deletions
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 8e43a7b69e64..bfee9b4c6661 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -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; |