summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/amba-pl011.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r--drivers/tty/serial/amba-pl011.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 9ae024025ff3..6800f5f26241 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -159,6 +159,7 @@ struct uart_amba_port {
unsigned int fifosize; /* vendor-specific */
unsigned int lcrh_tx; /* vendor-specific */
unsigned int lcrh_rx; /* vendor-specific */
+ unsigned int old_cr; /* state during shutdown */
bool autorts;
char type[12];
bool interrupt_may_hang; /* vendor-specific */
@@ -1411,7 +1412,9 @@ static int pl011_startup(struct uart_port *port)
while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
barrier();
- cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
+ /* restore RTS and DTR */
+ cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
+ cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
writew(cr, uap->port.membase + UART011_CR);
/* Clear pending error interrupts */
@@ -1469,6 +1472,7 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap,
static void pl011_shutdown(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
+ unsigned int cr;
/*
* disable all interrupts
@@ -1488,9 +1492,16 @@ static void pl011_shutdown(struct uart_port *port)
/*
* disable the port
+ * disable the port. It should not disable RTS and DTR.
+ * Also RTS and DTR state should be preserved to restore
+ * it during startup().
*/
uap->autorts = false;
- writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR);
+ 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);
/*
* disable break condition and fifos
@@ -1740,9 +1751,19 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_amba_port *uap = amba_ports[co->index];
unsigned int status, old_cr, new_cr;
+ unsigned long flags;
+ int locked = 1;
clk_enable(uap->clk);
+ local_irq_save(flags);
+ if (uap->port.sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock(&uap->port.lock);
+ else
+ spin_lock(&uap->port.lock);
+
/*
* First save the CR then disable the interrupts
*/
@@ -1762,6 +1783,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
} while (status & UART01x_FR_BUSY);
writew(old_cr, uap->port.membase + UART011_CR);
+ if (locked)
+ spin_unlock(&uap->port.lock);
+ local_irq_restore(flags);
+
clk_disable(uap->clk);
}
@@ -1905,6 +1930,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
uap->vendor = vendor;
uap->lcrh_rx = vendor->lcrh_rx;
uap->lcrh_tx = vendor->lcrh_tx;
+ uap->old_cr = 0;
uap->fifosize = vendor->fifosize;
uap->interrupt_may_hang = vendor->interrupt_may_hang;
uap->port.dev = &dev->dev;