From ac3e3fb424d44109dda3b1a3459e1b30fa60ac4a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 2 Jun 2010 20:40:22 +0100 Subject: [PATCH] ARM: 6158/2: PL011 baudrate extension for ST-Ericssons derivative Implementation of the ST-Ericsson baudrate extension in the PL011 block. In this modified variant it is possible to change the sampling factor from 16 to 8, and thanks to this we can get higher baudrates while still using the same peripheral clock. Also replace the simple division to determine the baud divisor with DIV_ROUND_CLOSEST() rather than a simple integer division. Cc: Alessandro Rubini Cc: Jerzy Kasenberg Signed-off-by: Marcin Mielczarczyk Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/serial/amba-pl011.c | 27 +++++++++++++++++++++++++-- include/linux/amba/serial.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index 5644cf2385bb..f67e09da6d33 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -74,6 +74,7 @@ struct uart_amba_port { unsigned int ifls; /* vendor-specific */ unsigned int lcrh_tx; /* vendor-specific */ unsigned int lcrh_rx; /* vendor-specific */ + bool oversampling; /* vendor-specific */ bool autorts; }; @@ -83,6 +84,7 @@ struct vendor_data { unsigned int fifosize; unsigned int lcrh_tx; unsigned int lcrh_rx; + bool oversampling; }; static struct vendor_data vendor_arm = { @@ -90,6 +92,7 @@ static struct vendor_data vendor_arm = { .fifosize = 16, .lcrh_tx = UART011_LCRH, .lcrh_rx = UART011_LCRH, + .oversampling = false, }; static struct vendor_data vendor_st = { @@ -97,6 +100,7 @@ static struct vendor_data vendor_st = { .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) @@ -499,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: @@ -579,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); @@ -744,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; + } } } @@ -839,6 +861,7 @@ 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; diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 93c96a66c518..e1b634b635f2 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -86,6 +86,7 @@ #define UART010_CR_TIE 0x0020 #define UART010_CR_RIE 0x0010 #define UART010_CR_MSIE 0x0008 +#define ST_UART011_CR_OVSFACT 0x0008 /* Oversampling factor */ #define UART01x_CR_IIRLP 0x0004 /* SIR low power mode */ #define UART01x_CR_SIREN 0x0002 /* SIR enable */ #define UART01x_CR_UARTEN 0x0001 /* UART enable */