Commit 129a45b1 authored by Matt Redfearn's avatar Matt Redfearn Committed by Greg Kroah-Hartman

serial: 8250_ingenic: Enable hardware flow control

The Ingenic UART is similar to a standard 16550, but hardware flow control
requires setting a couple of additional, non-standard bits in the MCR.

The non-standard "modem control enable" and "hardware flow control
mode" bits are set when writing to the MCR register, based
on whether the modem control interrupt is active.

Additionally the non-16550 compliant parts of the uart need to be
masked from higher layers.

Tested on Ingenic JZ4780 on MIPS Creator Ci20 board
Signed-off-by: default avatarMatt Redfearn <matt.redfearn@imgtec.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 20b5af93
...@@ -34,6 +34,9 @@ struct ingenic_uart_data { ...@@ -34,6 +34,9 @@ struct ingenic_uart_data {
#define UART_FCR_UME BIT(4) #define UART_FCR_UME BIT(4)
#define UART_MCR_MDCE BIT(7)
#define UART_MCR_FCM BIT(6)
static struct earlycon_device *early_device; static struct earlycon_device *early_device;
static uint8_t __init early_in(struct uart_port *port, int offset) static uint8_t __init early_in(struct uart_port *port, int offset)
...@@ -129,6 +132,8 @@ OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", ...@@ -129,6 +132,8 @@ OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
{ {
int ier;
switch (offset) { switch (offset) {
case UART_FCR: case UART_FCR:
/* UART module enable */ /* UART module enable */
...@@ -136,9 +141,22 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) ...@@ -136,9 +141,22 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
break; break;
case UART_IER: case UART_IER:
/* Enable receive timeout interrupt with the
* receive line status interrupt */
value |= (value & 0x4) << 2; value |= (value & 0x4) << 2;
break; break;
case UART_MCR:
/* If we have enabled modem status IRQs we should enable modem
* mode. */
ier = p->serial_in(p, UART_IER);
if (ier & UART_IER_MSI)
value |= UART_MCR_MDCE | UART_MCR_FCM;
else
value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
break;
default: default:
break; break;
} }
...@@ -146,6 +164,28 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) ...@@ -146,6 +164,28 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
writeb(value, p->membase + (offset << p->regshift)); writeb(value, p->membase + (offset << p->regshift));
} }
static unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset)
{
unsigned int value;
value = readb(p->membase + (offset << p->regshift));
/* Hide non-16550 compliant bits from higher levels */
switch (offset) {
case UART_FCR:
value &= ~UART_FCR_UME;
break;
case UART_MCR:
value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
break;
default:
break;
}
return value;
}
static int ingenic_uart_probe(struct platform_device *pdev) static int ingenic_uart_probe(struct platform_device *pdev)
{ {
struct uart_8250_port uart = {}; struct uart_8250_port uart = {};
...@@ -170,6 +210,7 @@ static int ingenic_uart_probe(struct platform_device *pdev) ...@@ -170,6 +210,7 @@ static int ingenic_uart_probe(struct platform_device *pdev)
uart.port.mapbase = regs->start; uart.port.mapbase = regs->start;
uart.port.regshift = 2; uart.port.regshift = 2;
uart.port.serial_out = ingenic_uart_serial_out; uart.port.serial_out = ingenic_uart_serial_out;
uart.port.serial_in = ingenic_uart_serial_in;
uart.port.irq = irq->start; uart.port.irq = irq->start;
uart.port.dev = &pdev->dev; uart.port.dev = &pdev->dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment