diff options
author | Matt Schulte <matts@commtech-fastcom.com> | 2012-11-19 09:12:04 -0600 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-11-21 15:37:46 -0800 |
commit | dc96efb72054985c0912f831da009a2da4e9f6dd (patch) | |
tree | 15a27b27040eb43fa2ff161d536a593623211e97 /drivers/tty/serial/8250/8250.c | |
parent | ae8d8a146725a966bd7c59c94f4d0016dcf7a04f (diff) | |
download | lwn-dc96efb72054985c0912f831da009a2da4e9f6dd.tar.gz lwn-dc96efb72054985c0912f831da009a2da4e9f6dd.zip |
Serial: Add support for new devices: Exar's XR17V35x family of multi-port PCIe UARTs
Add support for new devices: Exar's XR17V35x family of multi-port PCIe UARTs.
Signed-off-by: Matt Schulte <matts@commtech-fastcom.com>
Acked-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/8250/8250.c')
-rw-r--r-- | drivers/tty/serial/8250/8250.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 2af83a246499..3624df674a31 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -282,6 +282,15 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, }, + [PORT_XR17V35X] = { + .name = "XR17V35X", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11 | + UART_FCR_T_TRIG_11, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP, + }, [PORT_LPC3220] = { .name = "LPC3220", .fifo_size = 64, @@ -455,6 +464,7 @@ static void io_serial_out(struct uart_port *p, int offset, int value) } static int serial8250_default_handle_irq(struct uart_port *port); +static int exar_handle_irq(struct uart_port *port); static void set_io_from_upio(struct uart_port *p) { @@ -574,6 +584,18 @@ EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); */ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { + /* + * Exar UARTs have a SLEEP register that enables or disables + * each UART to enter sleep mode separately. On the XR17V35x the + * register is accessible to each UART at the UART_EXAR_SLEEP + * offset but the UART channel may only write to the corresponding + * bit. + */ + if (p->port.type == PORT_XR17V35X) { + serial_out(p, UART_EXAR_SLEEP, 0xff); + return; + } + if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); @@ -882,6 +904,27 @@ static void autoconfig_16550a(struct uart_8250_port *up) up->capabilities |= UART_CAP_FIFO; /* + * XR17V35x UARTs have an extra divisor register, DLD + * that gets enabled with when DLAB is set which will + * cause the device to incorrectly match and assign + * port type to PORT_16650. The EFR for this UART is + * found at offset 0x09. Instead check the Deice ID (DVID) + * register for a 2, 4 or 8 port UART. + */ + status1 = serial_in(up, UART_EXAR_DVID); + if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) { + if (up->port.flags & UPF_EXAR_EFR) { + DEBUG_AUTOCONF("Exar XR17V35x "); + up->port.type = PORT_XR17V35X; + up->capabilities |= UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP; + + return; + } + + } + + /* * Check for presence of the EFR when DLAB is set. * Only ST16C650V1 UARTs pass this test. */ @@ -1516,6 +1559,30 @@ static int serial8250_default_handle_irq(struct uart_port *port) } /* + * These Exar UARTs have an extra interrupt indicator that could + * fire for a few unimplemented interrupts. One of which is a + * wakeup event when coming out of sleep. Put this here just + * to be on the safe side that these interrupts don't go unhandled. + */ +static int exar_handle_irq(struct uart_port *port) +{ + unsigned char int0, int1, int2, int3; + unsigned int iir = serial_port_in(port, UART_IIR); + int ret; + + ret = serial8250_handle_irq(port, iir); + + if (port->type == PORT_XR17V35X) { + int0 = serial_port_in(port, 0x80); + int1 = serial_port_in(port, 0x81); + int2 = serial_port_in(port, 0x82); + int3 = serial_port_in(port, 0x83); + } + + return ret; +} + +/* * This is the serial driver's interrupt routine. * * Arjan thinks the old way was overly complex, so it got simplified. @@ -2614,6 +2681,10 @@ static void serial8250_config_port(struct uart_port *port, int flags) serial8250_release_rsa_resource(up); if (port->type == PORT_UNKNOWN) serial8250_release_std_resource(up); + + /* Fixme: probably not the best place for this */ + if (port->type == PORT_XR17V35X) + port->handle_irq = exar_handle_irq; } static int |