Skip to content

Commit

Permalink
tty: amba-pl011: define flag register bits for ZTE device
Browse files Browse the repository at this point in the history
For some reason we do not really understand, ZTE hardware designers
choose to define PL011 Flag Register bit positions differently from
standard ones as below.

	Bit		Standard	ZTE
	-----------------------------------
	CTS		0		1
	DSR		1		3
	BUSY		3		8
	RI		8		0

Let's define these bits into vendor data and get ZTE PL011 supported
properly.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Shawn Guo authored and Greg Kroah-Hartman committed Aug 31, 2016
1 parent 694d0d0 commit 0e125a5
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 10 deletions.
45 changes: 35 additions & 10 deletions drivers/tty/serial/amba-pl011.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ static u16 pl011_std_offsets[REG_ARRAY_SIZE] = {
struct vendor_data {
const u16 *reg_offset;
unsigned int ifls;
unsigned int fr_busy;
unsigned int fr_dsr;
unsigned int fr_cts;
unsigned int fr_ri;
bool access_32b;
bool oversampling;
bool dma_threshold;
Expand All @@ -111,6 +115,10 @@ static unsigned int get_fifosize_arm(struct amba_device *dev)
static struct vendor_data vendor_arm = {
.reg_offset = pl011_std_offsets,
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
.fr_busy = UART01x_FR_BUSY,
.fr_dsr = UART01x_FR_DSR,
.fr_cts = UART01x_FR_CTS,
.fr_ri = UART011_FR_RI,
.oversampling = false,
.dma_threshold = false,
.cts_event_workaround = false,
Expand All @@ -121,6 +129,10 @@ static struct vendor_data vendor_arm = {

static struct vendor_data vendor_sbsa = {
.reg_offset = pl011_std_offsets,
.fr_busy = UART01x_FR_BUSY,
.fr_dsr = UART01x_FR_DSR,
.fr_cts = UART01x_FR_CTS,
.fr_ri = UART011_FR_RI,
.access_32b = true,
.oversampling = false,
.dma_threshold = false,
Expand Down Expand Up @@ -164,6 +176,10 @@ static unsigned int get_fifosize_st(struct amba_device *dev)
static struct vendor_data vendor_st = {
.reg_offset = pl011_st_offsets,
.ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
.fr_busy = UART01x_FR_BUSY,
.fr_dsr = UART01x_FR_DSR,
.fr_cts = UART01x_FR_CTS,
.fr_ri = UART011_FR_RI,
.oversampling = true,
.dma_threshold = true,
.cts_event_workaround = true,
Expand Down Expand Up @@ -192,6 +208,10 @@ static struct vendor_data vendor_zte __maybe_unused = {
.reg_offset = pl011_zte_offsets,
.access_32b = true,
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
.fr_busy = ZX_UART01x_FR_BUSY,
.fr_dsr = ZX_UART01x_FR_DSR,
.fr_cts = ZX_UART01x_FR_CTS,
.fr_ri = ZX_UART011_FR_RI,
.get_fifosize = get_fifosize_arm,
};

Expand Down Expand Up @@ -1167,7 +1187,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap)
return;

/* Disable RX and TX DMA */
while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY)
while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy)
cpu_relax();

spin_lock_irq(&uap->port.lock);
Expand Down Expand Up @@ -1416,11 +1436,12 @@ static void pl011_modem_status(struct uart_amba_port *uap)
if (delta & UART01x_FR_DCD)
uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);

if (delta & UART01x_FR_DSR)
if (delta & uap->vendor->fr_dsr)
uap->port.icount.dsr++;

if (delta & UART01x_FR_CTS)
uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
if (delta & uap->vendor->fr_cts)
uart_handle_cts_change(&uap->port,
status & uap->vendor->fr_cts);

wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
}
Expand Down Expand Up @@ -1493,7 +1514,8 @@ static unsigned int pl011_tx_empty(struct uart_port *port)
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
unsigned int status = pl011_read(uap, REG_FR);
return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ?
0 : TIOCSER_TEMT;
}

static unsigned int pl011_get_mctrl(struct uart_port *port)
Expand All @@ -1508,9 +1530,9 @@ static unsigned int pl011_get_mctrl(struct uart_port *port)
result |= tiocmbit

TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR);
TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR);
TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS);
TIOCMBIT(UART011_FR_RI, TIOCM_RNG);
TIOCMBIT(uap->vendor->fr_dsr, TIOCM_DSR);
TIOCMBIT(uap->vendor->fr_cts, TIOCM_CTS);
TIOCMBIT(uap->vendor->fr_ri, TIOCM_RNG);
#undef TIOCMBIT
return result;
}
Expand Down Expand Up @@ -2191,7 +2213,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
* Finally, wait for transmitter to become empty
* and restore the TCR
*/
while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY)
while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy)
cpu_relax();
if (!uap->vendor->always_enabled)
pl011_write(old_cr, uap, REG_CR);
Expand Down Expand Up @@ -2303,13 +2325,16 @@ static struct console amba_console = {

static void pl011_putc(struct uart_port *port, int c)
{
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);

while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
cpu_relax();
if (port->iotype == UPIO_MEM32)
writel(c, port->membase + UART01x_DR);
else
writeb(c, port->membase + UART01x_DR);
while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY)
while (readl(port->membase + UART01x_FR) & uap->vendor->fr_busy)
cpu_relax();
}

Expand Down
9 changes: 9 additions & 0 deletions include/linux/amba/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@
#define UART01x_FR_CTS 0x001
#define UART01x_FR_TMSK (UART01x_FR_TXFF + UART01x_FR_BUSY)

/*
* Some bits of Flag Register on ZTE device have different position from
* standard ones.
*/
#define ZX_UART01x_FR_BUSY 0x100
#define ZX_UART01x_FR_DSR 0x008
#define ZX_UART01x_FR_CTS 0x002
#define ZX_UART011_FR_RI 0x001

#define UART011_CR_CTSEN 0x8000 /* CTS hardware flow control */
#define UART011_CR_RTSEN 0x4000 /* RTS hardware flow control */
#define UART011_CR_OUT2 0x2000 /* OUT2 */
Expand Down

0 comments on commit 0e125a5

Please sign in to comment.