|
@@ -282,6 +282,15 @@ static const struct serial8250_config uart_config[] = {
|
|
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
|
|
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
|
|
.flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR,
|
|
.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] = {
|
|
[PORT_LPC3220] = {
|
|
.name = "LPC3220",
|
|
.name = "LPC3220",
|
|
.fifo_size = 64,
|
|
.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 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)
|
|
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)
|
|
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_SLEEP) {
|
|
if (p->capabilities & UART_CAP_EFR) {
|
|
if (p->capabilities & UART_CAP_EFR) {
|
|
serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
@@ -881,6 +903,27 @@ static void autoconfig_16550a(struct uart_8250_port *up)
|
|
up->port.type = PORT_16550A;
|
|
up->port.type = PORT_16550A;
|
|
up->capabilities |= UART_CAP_FIFO;
|
|
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.
|
|
* Check for presence of the EFR when DLAB is set.
|
|
* Only ST16C650V1 UARTs pass this test.
|
|
* Only ST16C650V1 UARTs pass this test.
|
|
@@ -1515,6 +1558,30 @@ static int serial8250_default_handle_irq(struct uart_port *port)
|
|
return serial8250_handle_irq(port, iir);
|
|
return serial8250_handle_irq(port, iir);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * 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.
|
|
* This is the serial driver's interrupt routine.
|
|
*
|
|
*
|
|
@@ -2614,6 +2681,10 @@ static void serial8250_config_port(struct uart_port *port, int flags)
|
|
serial8250_release_rsa_resource(up);
|
|
serial8250_release_rsa_resource(up);
|
|
if (port->type == PORT_UNKNOWN)
|
|
if (port->type == PORT_UNKNOWN)
|
|
serial8250_release_std_resource(up);
|
|
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
|
|
static int
|