diff options
Diffstat (limited to 'drivers/serial/amba-pl011.c')
-rw-r--r-- | drivers/serial/amba-pl011.c | 90 |
1 files changed, 76 insertions, 14 deletions
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index eb4cb480b93e..6ca7a44f29c2 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -69,9 +69,12 @@ struct uart_amba_port { struct uart_port port; struct clk *clk; - unsigned int im; /* interrupt mask */ + unsigned int im; /* interrupt mask */ unsigned int old_status; - unsigned int ifls; /* vendor-specific */ + unsigned int ifls; /* vendor-specific */ + unsigned int lcrh_tx; /* vendor-specific */ + unsigned int lcrh_rx; /* vendor-specific */ + bool oversampling; /* vendor-specific */ bool autorts; }; @@ -79,16 +82,25 @@ struct uart_amba_port { struct vendor_data { unsigned int ifls; unsigned int fifosize; + unsigned int lcrh_tx; + unsigned int lcrh_rx; + bool oversampling; }; static struct vendor_data vendor_arm = { .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, .fifosize = 16, + .lcrh_tx = UART011_LCRH, + .lcrh_rx = UART011_LCRH, + .oversampling = false, }; static struct vendor_data vendor_st = { .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, .fifosize = 64, + .lcrh_tx = ST_UART011_LCRH_TX, + .lcrh_rx = ST_UART011_LCRH_RX, + .oversampling = true, }; static void pl011_stop_tx(struct uart_port *port) @@ -327,12 +339,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) unsigned int lcr_h; spin_lock_irqsave(&uap->port.lock, flags); - lcr_h = readw(uap->port.membase + UART011_LCRH); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; - writew(lcr_h, uap->port.membase + UART011_LCRH); + writew(lcr_h, uap->port.membase + uap->lcrh_tx); spin_unlock_irqrestore(&uap->port.lock, flags); } @@ -393,7 +405,17 @@ static int pl011_startup(struct uart_port *port) writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); writew(1, uap->port.membase + UART011_IBRD); - writew(0, uap->port.membase + UART011_LCRH); + writew(0, uap->port.membase + uap->lcrh_rx); + if (uap->lcrh_tx != uap->lcrh_rx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(0, uap->port.membase + uap->lcrh_tx); + } writew(0, uap->port.membase + UART01x_DR); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); @@ -422,10 +444,19 @@ static int pl011_startup(struct uart_port *port) return retval; } +static void pl011_shutdown_channel(struct uart_amba_port *uap, + unsigned int lcrh) +{ + unsigned long val; + + val = readw(uap->port.membase + lcrh); + val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); + writew(val, uap->port.membase + lcrh); +} + static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned long val; /* * disable all interrupts @@ -450,9 +481,9 @@ static void pl011_shutdown(struct uart_port *port) /* * disable break condition and fifos */ - val = readw(uap->port.membase + UART011_LCRH); - val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); - writew(val, uap->port.membase + UART011_LCRH); + pl011_shutdown_channel(uap, uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) + pl011_shutdown_channel(uap, uap->lcrh_tx); /* * Shut down the clock producer @@ -472,8 +503,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = port->uartclk * 4 / baud; + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk/(uap->oversampling ? 8 : 16)); + + if (baud > port->uartclk/16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); switch (termios->c_cflag & CSIZE) { case CS5: @@ -552,6 +588,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, uap->autorts = false; } + if (uap->oversampling) { + if (baud > port->uartclk/16) + old_cr |= ST_UART011_CR_OVSFACT; + else + old_cr &= ~ST_UART011_CR_OVSFACT; + } + /* Set baud rate */ writew(quot & 0x3f, port->membase + UART011_FBRD); writew(quot >> 6, port->membase + UART011_IBRD); @@ -561,7 +604,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L * ----------^----------^----------^----------^----- */ - writew(lcr_h, port->membase + UART011_LCRH); + writew(lcr_h, port->membase + uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(lcr_h, port->membase + uap->lcrh_tx); + } writew(old_cr, port->membase + UART011_CR); spin_unlock_irqrestore(&port->lock, flags); @@ -688,7 +741,7 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) { unsigned int lcr_h, ibrd, fbrd; - lcr_h = readw(uap->port.membase + UART011_LCRH); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); *parity = 'n'; if (lcr_h & UART01x_LCRH_PEN) { @@ -707,6 +760,12 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, fbrd = readw(uap->port.membase + UART011_FBRD); *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + + if (uap->oversampling) { + if (readw(uap->port.membase + UART011_CR) + & ST_UART011_CR_OVSFACT) + *baud *= 2; + } } } @@ -800,6 +859,9 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id) } uap->ifls = vendor->ifls; + uap->lcrh_rx = vendor->lcrh_rx; + uap->lcrh_tx = vendor->lcrh_tx; + uap->oversampling = vendor->oversampling; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; @@ -868,7 +930,7 @@ static int pl011_resume(struct amba_device *dev) } #endif -static struct amba_id pl011_ids[] __initdata = { +static struct amba_id pl011_ids[] = { { .id = 0x00041011, .mask = 0x000fffff, |