diff options
| author | Stepan Moskovchenko <stepanm@codeaurora.org> | 2010-12-21 15:38:05 -0500 |
|---|---|---|
| committer | David Brown <davidb@codeaurora.org> | 2011-01-21 18:52:55 -0500 |
| commit | ec8f29e70edceb93c021148a99a5c3889cdc1b08 (patch) | |
| tree | f5089d43011477da76ba99c882d601777dbf78b6 | |
| parent | d41cb8c95681345ded5ef1e78d235d06d68baee2 (diff) | |
serial: msm: Add support for UARTDM cores
Add support for the next-generation version of the MSM UART
to the msm_serial driver. This version of the hardware is
similar to the original version, but has some DMA
capabilities that are used in PIO mode in this driver.
Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
| -rw-r--r-- | drivers/serial/msm_serial.c | 286 | ||||
| -rw-r--r-- | drivers/serial/msm_serial.h | 28 |
2 files changed, 269 insertions, 45 deletions
diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c index 8e43a7b69e64..bfee9b4c6661 100644 --- a/drivers/serial/msm_serial.c +++ b/drivers/serial/msm_serial.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2007 Google, Inc. | 4 | * Copyright (C) 2007 Google, Inc. |
| 5 | * Author: Robert Love <rlove@google.com> | 5 | * Author: Robert Love <rlove@google.com> |
| 6 | * Copyright (c) 2011, Code Aurora Forum. All rights reserved. | ||
| 6 | * | 7 | * |
| 7 | * This software is licensed under the terms of the GNU General Public | 8 | * This software is licensed under the terms of the GNU General Public |
| 8 | * License version 2, as published by the Free Software Foundation, and | 9 | * License version 2, as published by the Free Software Foundation, and |
| @@ -31,6 +32,7 @@ | |||
| 31 | #include <linux/serial.h> | 32 | #include <linux/serial.h> |
| 32 | #include <linux/clk.h> | 33 | #include <linux/clk.h> |
| 33 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
| 35 | #include <linux/delay.h> | ||
| 34 | 36 | ||
| 35 | #include "msm_serial.h" | 37 | #include "msm_serial.h" |
| 36 | 38 | ||
| @@ -38,9 +40,20 @@ struct msm_port { | |||
| 38 | struct uart_port uart; | 40 | struct uart_port uart; |
| 39 | char name[16]; | 41 | char name[16]; |
| 40 | struct clk *clk; | 42 | struct clk *clk; |
| 43 | struct clk *pclk; | ||
| 41 | unsigned int imr; | 44 | unsigned int imr; |
| 45 | unsigned int *gsbi_base; | ||
| 46 | int is_uartdm; | ||
| 47 | unsigned int old_snap_state; | ||
| 42 | }; | 48 | }; |
| 43 | 49 | ||
| 50 | static inline void wait_for_xmitr(struct uart_port *port, int bits) | ||
| 51 | { | ||
| 52 | if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) | ||
| 53 | while ((msm_read(port, UART_ISR) & bits) != bits) | ||
| 54 | cpu_relax(); | ||
| 55 | } | ||
| 56 | |||
| 44 | static void msm_stop_tx(struct uart_port *port) | 57 | static void msm_stop_tx(struct uart_port *port) |
| 45 | { | 58 | { |
| 46 | struct msm_port *msm_port = UART_TO_MSM(port); | 59 | struct msm_port *msm_port = UART_TO_MSM(port); |
| @@ -73,6 +86,61 @@ static void msm_enable_ms(struct uart_port *port) | |||
| 73 | msm_write(port, msm_port->imr, UART_IMR); | 86 | msm_write(port, msm_port->imr, UART_IMR); |
| 74 | } | 87 | } |
| 75 | 88 | ||
| 89 | static void handle_rx_dm(struct uart_port *port, unsigned int misr) | ||
| 90 | { | ||
| 91 | struct tty_struct *tty = port->state->port.tty; | ||
| 92 | unsigned int sr; | ||
| 93 | int count = 0; | ||
| 94 | struct msm_port *msm_port = UART_TO_MSM(port); | ||
| 95 | |||
| 96 | if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) { | ||
| 97 | port->icount.overrun++; | ||
| 98 | tty_insert_flip_char(tty, 0, TTY_OVERRUN); | ||
| 99 | msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); | ||
| 100 | } | ||
| 101 | |||
| 102 | if (misr & UART_IMR_RXSTALE) { | ||
| 103 | count = msm_read(port, UARTDM_RX_TOTAL_SNAP) - | ||
| 104 | msm_port->old_snap_state; | ||
| 105 | msm_port->old_snap_state = 0; | ||
| 106 | } else { | ||
| 107 | count = 4 * (msm_read(port, UART_RFWR)); | ||
| 108 | msm_port->old_snap_state += count; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* TODO: Precise error reporting */ | ||
| 112 | |||
| 113 | port->icount.rx += count; | ||
| 114 | |||
| 115 | while (count > 0) { | ||
| 116 | unsigned int c; | ||
| 117 | |||
| 118 | sr = msm_read(port, UART_SR); | ||
| 119 | if ((sr & UART_SR_RX_READY) == 0) { | ||
| 120 | msm_port->old_snap_state -= count; | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | c = msm_read(port, UARTDM_RF); | ||
| 124 | if (sr & UART_SR_RX_BREAK) { | ||
| 125 | port->icount.brk++; | ||
| 126 | if (uart_handle_break(port)) | ||
| 127 | continue; | ||
| 128 | } else if (sr & UART_SR_PAR_FRAME_ERR) | ||
| 129 | port->icount.frame++; | ||
| 130 | |||
| 131 | /* TODO: handle sysrq */ | ||
| 132 | tty_insert_flip_string(tty, (char *) &c, | ||
| 133 | (count > 4) ? 4 : count); | ||
| 134 | count -= 4; | ||
| 135 | } | ||
| 136 | |||
| 137 | tty_flip_buffer_push(tty); | ||
| 138 | if (misr & (UART_IMR_RXSTALE)) | ||
| 139 | msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); | ||
| 140 | msm_write(port, 0xFFFFFF, UARTDM_DMRX); | ||
| 141 | msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR); | ||
| 142 | } | ||
| 143 | |||
| 76 | static void handle_rx(struct uart_port *port) | 144 | static void handle_rx(struct uart_port *port) |
| 77 | { | 145 | { |
| 78 | struct tty_struct *tty = port->state->port.tty; | 146 | struct tty_struct *tty = port->state->port.tty; |
| @@ -121,6 +189,12 @@ static void handle_rx(struct uart_port *port) | |||
| 121 | tty_flip_buffer_push(tty); | 189 | tty_flip_buffer_push(tty); |
| 122 | } | 190 | } |
| 123 | 191 | ||
| 192 | static void reset_dm_count(struct uart_port *port) | ||
| 193 | { | ||
| 194 | wait_for_xmitr(port, UART_ISR_TX_READY); | ||
| 195 | msm_write(port, 1, UARTDM_NCF_TX); | ||
| 196 | } | ||
| 197 | |||
| 124 | static void handle_tx(struct uart_port *port) | 198 | static void handle_tx(struct uart_port *port) |
| 125 | { | 199 | { |
| 126 | struct circ_buf *xmit = &port->state->xmit; | 200 | struct circ_buf *xmit = &port->state->xmit; |
| @@ -128,11 +202,18 @@ static void handle_tx(struct uart_port *port) | |||
| 128 | int sent_tx; | 202 | int sent_tx; |
| 129 | 203 | ||
| 130 | if (port->x_char) { | 204 | if (port->x_char) { |
| 131 | msm_write(port, port->x_char, UART_TF); | 205 | if (msm_port->is_uartdm) |
| 206 | reset_dm_count(port); | ||
| 207 | |||
| 208 | msm_write(port, port->x_char, | ||
| 209 | msm_port->is_uartdm ? UARTDM_TF : UART_TF); | ||
| 132 | port->icount.tx++; | 210 | port->icount.tx++; |
| 133 | port->x_char = 0; | 211 | port->x_char = 0; |
| 134 | } | 212 | } |
| 135 | 213 | ||
| 214 | if (msm_port->is_uartdm) | ||
| 215 | reset_dm_count(port); | ||
| 216 | |||
| 136 | while (msm_read(port, UART_SR) & UART_SR_TX_READY) { | 217 | while (msm_read(port, UART_SR) & UART_SR_TX_READY) { |
| 137 | if (uart_circ_empty(xmit)) { | 218 | if (uart_circ_empty(xmit)) { |
| 138 | /* disable tx interrupts */ | 219 | /* disable tx interrupts */ |
| @@ -140,8 +221,11 @@ static void handle_tx(struct uart_port *port) | |||
| 140 | msm_write(port, msm_port->imr, UART_IMR); | 221 | msm_write(port, msm_port->imr, UART_IMR); |
| 141 | break; | 222 | break; |
| 142 | } | 223 | } |
| 224 | msm_write(port, xmit->buf[xmit->tail], | ||
| 225 | msm_port->is_uartdm ? UARTDM_TF : UART_TF); | ||
| 143 | 226 | ||
| 144 | msm_write(port, xmit->buf[xmit->tail], UART_TF); | 227 | if (msm_port->is_uartdm) |
| 228 | reset_dm_count(port); | ||
| 145 | 229 | ||
| 146 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | 230 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); |
| 147 | port->icount.tx++; | 231 | port->icount.tx++; |
| @@ -169,8 +253,12 @@ static irqreturn_t msm_irq(int irq, void *dev_id) | |||
| 169 | misr = msm_read(port, UART_MISR); | 253 | misr = msm_read(port, UART_MISR); |
| 170 | msm_write(port, 0, UART_IMR); /* disable interrupt */ | 254 | msm_write(port, 0, UART_IMR); /* disable interrupt */ |
| 171 | 255 | ||
| 172 | if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) | 256 | if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) { |
| 173 | handle_rx(port); | 257 | if (msm_port->is_uartdm) |
| 258 | handle_rx_dm(port, misr); | ||
| 259 | else | ||
| 260 | handle_rx(port); | ||
| 261 | } | ||
| 174 | if (misr & UART_IMR_TXLEV) | 262 | if (misr & UART_IMR_TXLEV) |
| 175 | handle_tx(port); | 263 | handle_tx(port); |
| 176 | if (misr & UART_IMR_DELTA_CTS) | 264 | if (misr & UART_IMR_DELTA_CTS) |
| @@ -192,10 +280,21 @@ static unsigned int msm_get_mctrl(struct uart_port *port) | |||
| 192 | return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; | 280 | return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; |
| 193 | } | 281 | } |
| 194 | 282 | ||
| 195 | static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) | 283 | |
| 284 | static void msm_reset(struct uart_port *port) | ||
| 196 | { | 285 | { |
