diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/atmel_serial.c | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 3320bcd92c0a..4d6b3c56d20e 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c | |||
@@ -114,6 +114,7 @@ struct atmel_uart_port { | |||
114 | struct uart_port uart; /* uart */ | 114 | struct uart_port uart; /* uart */ |
115 | struct clk *clk; /* uart clock */ | 115 | struct clk *clk; /* uart clock */ |
116 | unsigned short suspended; /* is port suspended? */ | 116 | unsigned short suspended; /* is port suspended? */ |
117 | int break_active; /* break being received */ | ||
117 | }; | 118 | }; |
118 | 119 | ||
119 | static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; | 120 | static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; |
@@ -252,6 +253,7 @@ static void atmel_break_ctl(struct uart_port *port, int break_state) | |||
252 | */ | 253 | */ |
253 | static void atmel_rx_chars(struct uart_port *port) | 254 | static void atmel_rx_chars(struct uart_port *port) |
254 | { | 255 | { |
256 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; | ||
255 | struct tty_struct *tty = port->info->tty; | 257 | struct tty_struct *tty = port->info->tty; |
256 | unsigned int status, ch, flg; | 258 | unsigned int status, ch, flg; |
257 | 259 | ||
@@ -267,13 +269,29 @@ static void atmel_rx_chars(struct uart_port *port) | |||
267 | * note that the error handling code is | 269 | * note that the error handling code is |
268 | * out of the main execution path | 270 | * out of the main execution path |
269 | */ | 271 | */ |
270 | if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME | ATMEL_US_OVRE | ATMEL_US_RXBRK))) { | 272 | if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME |
273 | | ATMEL_US_OVRE | ATMEL_US_RXBRK) | ||
274 | || atmel_port->break_active)) { | ||
271 | UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */ | 275 | UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */ |
272 | if (status & ATMEL_US_RXBRK) { | 276 | if (status & ATMEL_US_RXBRK |
277 | && !atmel_port->break_active) { | ||
273 | status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */ | 278 | status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */ |
274 | port->icount.brk++; | 279 | port->icount.brk++; |
280 | atmel_port->break_active = 1; | ||
281 | UART_PUT_IER(port, ATMEL_US_RXBRK); | ||
275 | if (uart_handle_break(port)) | 282 | if (uart_handle_break(port)) |
276 | goto ignore_char; | 283 | goto ignore_char; |
284 | } else { | ||
285 | /* | ||
286 | * This is either the end-of-break | ||
287 | * condition or we've received at | ||
288 | * least one character without RXBRK | ||
289 | * being set. In both cases, the next | ||
290 | * RXBRK will indicate start-of-break. | ||
291 | */ | ||
292 | UART_PUT_IDR(port, ATMEL_US_RXBRK); | ||
293 | status &= ~ATMEL_US_RXBRK; | ||
294 | atmel_port->break_active = 0; | ||
277 | } | 295 | } |
278 | if (status & ATMEL_US_PARE) | 296 | if (status & ATMEL_US_PARE) |
279 | port->icount.parity++; | 297 | port->icount.parity++; |
@@ -352,6 +370,16 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) | |||
352 | /* Interrupt receive */ | 370 | /* Interrupt receive */ |
353 | if (pending & ATMEL_US_RXRDY) | 371 | if (pending & ATMEL_US_RXRDY) |
354 | atmel_rx_chars(port); | 372 | atmel_rx_chars(port); |
373 | else if (pending & ATMEL_US_RXBRK) { | ||
374 | /* | ||
375 | * End of break detected. If it came along | ||
376 | * with a character, atmel_rx_chars will | ||
377 | * handle it. | ||
378 | */ | ||
379 | UART_PUT_CR(port, ATMEL_US_RSTSTA); | ||
380 | UART_PUT_IDR(port, ATMEL_US_RXBRK); | ||
381 | atmel_port->break_active = 0; | ||
382 | } | ||
355 | 383 | ||
356 | // TODO: All reads to CSR will clear these interrupts! | 384 | // TODO: All reads to CSR will clear these interrupts! |
357 | if (pending & ATMEL_US_RIIC) port->icount.rng++; | 385 | if (pending & ATMEL_US_RIIC) port->icount.rng++; |