| From fe43390702a1b5741fdf217063b05c7612b38303 Mon Sep 17 00:00:00 2001 |
| From: Jon Medhurst <tixy@linaro.org> |
| Date: Tue, 10 Dec 2013 10:18:58 +0000 |
| Subject: serial: amba-pl011: use port lock to guard control register access |
| |
| From: Jon Medhurst <tixy@linaro.org> |
| |
| commit fe43390702a1b5741fdf217063b05c7612b38303 upstream. |
| |
| When the pl011 is being used for a console, pl011_console_write forces |
| the control register (CR) to enable the UART for transmission and then |
| restores this to the original value afterwards. It does this while |
| holding the port lock. |
| |
| Unfortunately, when the uart is started or shutdown - say in response to |
| userland using the serial device for a terminal - then this updates the |
| control register without any locking. |
| |
| This means we can have |
| |
| pl011_console_write Save CR |
| pl011_startup Initialise CR, e.g. enable receive |
| pl011_console_write Restore old CR with receive not enabled |
| |
| this result is a serial port which doesn't respond to any input. |
| |
| A similar race in reverse could happen when the device is shutdown. |
| |
| We can fix these problems by taking the port lock when updating CR. |
| |
| Signed-off-by: Jon Medhurst <tixy@linaro.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/tty/serial/amba-pl011.c | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| --- a/drivers/tty/serial/amba-pl011.c |
| +++ b/drivers/tty/serial/amba-pl011.c |
| @@ -1543,6 +1543,8 @@ static int pl011_startup(struct uart_por |
| /* |
| * Provoke TX FIFO interrupt into asserting. |
| */ |
| + spin_lock_irq(&uap->port.lock); |
| + |
| cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE; |
| writew(cr, uap->port.membase + UART011_CR); |
| writew(0, uap->port.membase + UART011_FBRD); |
| @@ -1567,6 +1569,8 @@ static int pl011_startup(struct uart_por |
| cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; |
| writew(cr, uap->port.membase + UART011_CR); |
| |
| + spin_unlock_irq(&uap->port.lock); |
| + |
| /* |
| * initialise the old status of the modem signals |
| */ |
| @@ -1636,11 +1640,13 @@ static void pl011_shutdown(struct uart_p |
| * it during startup(). |
| */ |
| uap->autorts = false; |
| + spin_lock_irq(&uap->port.lock); |
| cr = readw(uap->port.membase + UART011_CR); |
| uap->old_cr = cr; |
| cr &= UART011_CR_RTS | UART011_CR_DTR; |
| cr |= UART01x_CR_UARTEN | UART011_CR_TXE; |
| writew(cr, uap->port.membase + UART011_CR); |
| + spin_unlock_irq(&uap->port.lock); |
| |
| /* |
| * disable break condition and fifos |