Commit c7bb4498 authored by Russell King's avatar Russell King

[SERIAL] Tidy up 8250 port type detection

The original port detection code was one large function with many
tests without any clear structure.

This cset cleans this up, splitting out the tests for 8250/16450
vs 16550 vs 16550A vs later ports, and adds comments about why
we test what we're testing.
parent 4524aad2
...@@ -344,10 +344,9 @@ static int size_fifo(struct uart_8250_port *up) ...@@ -344,10 +344,9 @@ static int size_fifo(struct uart_8250_port *up)
* *
* What evil have men's minds wrought... * What evil have men's minds wrought...
*/ */
static void static void autoconfig_has_efr(struct uart_8250_port *up)
autoconfig_startech_uarts(struct uart_8250_port *up)
{ {
unsigned char scratch, scratch2, scratch3, scratch4; unsigned char id1, id2, id3, rev, saved_dll, saved_dlm;
/* /*
* First we check to see if it's an Oxford Semiconductor UART. * First we check to see if it's an Oxford Semiconductor UART.
...@@ -356,31 +355,32 @@ autoconfig_startech_uarts(struct uart_8250_port *up) ...@@ -356,31 +355,32 @@ autoconfig_startech_uarts(struct uart_8250_port *up)
* Semiconductor clone chips lock up if you try writing to the * Semiconductor clone chips lock up if you try writing to the
* LSR register (which serial_icr_read does) * LSR register (which serial_icr_read does)
*/ */
if (up->port.type == PORT_16550A) {
/* /*
* EFR [4] must be set else this test fails * Check for Oxford Semiconductor 16C950.
* *
* This shouldn't be necessary, but Mike Hudson * EFR [4] must be set else this test fails.
* (Exoray@isys.ca) claims that it's needed for 952 *
* dual UART's (which are not recommended for new designs). * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca)
*/ * claims that it's needed for 952 dual UART's (which are not
up->acr = 0; * recommended for new designs).
serial_out(up, UART_LCR, 0xBF); */
serial_out(up, UART_EFR, 0x10); up->acr = 0;
serial_out(up, UART_LCR, 0x00); serial_out(up, UART_LCR, 0xBF);
/* Check for Oxford Semiconductor 16C950 */ serial_out(up, UART_EFR, UART_EFR_ECB);
scratch = serial_icr_read(up, UART_ID1); serial_out(up, UART_LCR, 0x00);
scratch2 = serial_icr_read(up, UART_ID2); id1 = serial_icr_read(up, UART_ID1);
scratch3 = serial_icr_read(up, UART_ID3); id2 = serial_icr_read(up, UART_ID2);
id3 = serial_icr_read(up, UART_ID3);
if (scratch == 0x16 && scratch2 == 0xC9 && rev = serial_icr_read(up, UART_REV);
(scratch3 == 0x50 || scratch3 == 0x52 ||
scratch3 == 0x54)) { DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev);
up->port.type = PORT_16C950;
up->rev = serial_icr_read(up, UART_REV) | if (id1 == 0x16 && id2 == 0xC9 &&
(scratch3 << 8); (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
return; up->port.type = PORT_16C950;
} up->rev = rev | (scratch3 << 8);
return;
} }
/* /*
...@@ -391,34 +391,28 @@ autoconfig_startech_uarts(struct uart_8250_port *up) ...@@ -391,34 +391,28 @@ autoconfig_startech_uarts(struct uart_8250_port *up)
* 0x12 - XR16C2850. * 0x12 - XR16C2850.
* 0x14 - XR16C854. * 0x14 - XR16C854.
*/ */
/* Save the DLL and DLM */
serial_outp(up, UART_LCR, UART_LCR_DLAB); serial_outp(up, UART_LCR, UART_LCR_DLAB);
scratch3 = serial_inp(up, UART_DLL); saved_dll = serial_inp(up, UART_DLL);
scratch4 = serial_inp(up, UART_DLM); saved_dlm = serial_inp(up, UART_DLM);
serial_outp(up, UART_DLL, 0); serial_outp(up, UART_DLL, 0);
serial_outp(up, UART_DLM, 0); serial_outp(up, UART_DLM, 0);
scratch2 = serial_inp(up, UART_DLL); id2 = serial_inp(up, UART_DLL);
scratch = serial_inp(up, UART_DLM); id1 = serial_inp(up, UART_DLM);
serial_outp(up, UART_LCR, 0); serial_outp(up, UART_DLL, saved_dll);
serial_outp(up, UART_DLM, saved_dlm);
DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2);
if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) { if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) {
if (scratch == 0x10) if (id1 == 0x10)
up->rev = scratch2; up->rev = id2;
up->port.type = PORT_16850; up->port.type = PORT_16850;
return; return;
} }
/* Restore the DLL and DLM */
serial_outp(up, UART_LCR, UART_LCR_DLAB);
serial_outp(up, UART_DLL, scratch3);
serial_outp(up, UART_DLM, scratch4);
serial_outp(up, UART_LCR, 0);
/* /*
* It wasn't an XR16C850.
*
* We distinguish between the '654 and the '650 by counting * We distinguish between the '654 and the '650 by counting
* how many bytes are in the FIFO. I'm using this for now, * how many bytes are in the FIFO. I'm using this for now,
* since that's the technique that was sent to me in the * since that's the technique that was sent to me in the
...@@ -431,6 +425,85 @@ autoconfig_startech_uarts(struct uart_8250_port *up) ...@@ -431,6 +425,85 @@ autoconfig_startech_uarts(struct uart_8250_port *up)
up->port.type = PORT_16650V2; up->port.type = PORT_16650V2;
} }
/*
* We detected a chip without a FIFO. Only two fall into
* this category - the original 8250 and the 16450. The
* 16450 has a scratch register (accessible with LCR=0)
*/
static void autoconfig_8250(struct uart_8250_port *up)
{
unsigned char scratch, status1, status2;
up->port.type = PORT_8250;
scratch = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, 0xa5);
status1 = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, 0x5a);
status2 = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, scratch);
if (status1 == 0xa5 && status2 == 0x5a)
up->port.type = PORT_16450;
}
/*
* We know that the chip has FIFOs. Does it have an EFR? The
* EFR is located in the same register position as the IIR and
* we know the top two bits of the IIR are currently set. The
* EFR should contain zero. Try to read the EFR.
*/
static void autoconfig_16550a(struct uart_8250_port *up)
{
unsigned char status1, status2;
up->port.type = PORT_16550A;
/*
* Check for presence of the EFR when DLAB is set.
* Only ST16C650V1 UARTs pass this test.
*/
serial_outp(up, UART_LCR, UART_LCR_DLAB);
if (serial_in(up, UART_EFR) == 0) {
DEBUG_AUTOCONF("EFRv1 ");
up->port.type = PORT_16650;
return;
}
/*
* Maybe it requires 0xbf to be written to the LCR.
* (other ST16C650V2 UARTs, TI16C752A, etc)
*/
serial_outp(up, UART_LCR, 0xBF);
if (serial_in(up, UART_EFR) == 0) {
DEBUG_AUTOCONF("EFRv2 ");
autoconfig_has_efr(up);
return;
}
/*
* No EFR. Try to detect a TI16750, which only sets bit 5 of
* the IIR when 64 byte FIFO mode is enabled when DLAB is set.
* Try setting it with and without DLAB set. Cheap clones
* set bit 5 without DLAB set.
*/
serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
status1 = serial_in(up, UART_IIR) >> 5;
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_outp(up, UART_LCR, UART_LCR_DLAB);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
status2 = serial_in(up, UART_IIR) >> 5;
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
if (status1 == 6 && status2 == 7) {
up->port.type = PORT_16750;
return;
}
}
/* /*
* This routine is called by rs_init() to initialize a specific serial * This routine is called by rs_init() to initialize a specific serial
* port. It determines what type of UART chip this serial port is * port. It determines what type of UART chip this serial port is
...@@ -440,16 +513,16 @@ autoconfig_startech_uarts(struct uart_8250_port *up) ...@@ -440,16 +513,16 @@ autoconfig_startech_uarts(struct uart_8250_port *up)
*/ */
static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
{ {
unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char status1, scratch, scratch2, scratch3;
unsigned char save_lcr, save_mcr; unsigned char save_lcr, save_mcr;
unsigned long flags; unsigned long flags;
DEBUG_AUTOCONF("Testing ttyS%d (0x%04x, 0x%08lx)...\n",
up->port.line, up->port.iobase, up->port.membase);
if (!up->port.iobase && !up->port.membase) if (!up->port.iobase && !up->port.membase)
return; return;
DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%08lx): ",
up->port.line, up->port.iobase, up->port.membase);
/* /*
* We really do need global IRQs disabled here - we're going to * We really do need global IRQs disabled here - we're going to
* be frobbing the chips IRQ enable register to see if it exists. * be frobbing the chips IRQ enable register to see if it exists.
...@@ -467,6 +540,9 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) ...@@ -467,6 +540,9 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
* assumption is that 0x80 is a non-existent port; * assumption is that 0x80 is a non-existent port;
* which should be safe since include/asm/io.h also * which should be safe since include/asm/io.h also
* makes this assumption. * makes this assumption.
*
* Note: this is safe as long as MCR bit 4 is clear
* and the device is in "PC" mode.
*/ */
scratch = serial_inp(up, UART_IER); scratch = serial_inp(up, UART_IER);
serial_outp(up, UART_IER, 0); serial_outp(up, UART_IER, 0);
...@@ -484,9 +560,8 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) ...@@ -484,9 +560,8 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
/* /*
* We failed; there's nothing here * We failed; there's nothing here
*/ */
DEBUG_AUTOCONF("serial: ttyS%d: simple autoconfig " DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
"failed (%02x, %02x)\n", scratch2, scratch3);
up->port.line, scratch2, scratch3);
goto out; goto out;
} }
} }
...@@ -508,64 +583,45 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) ...@@ -508,64 +583,45 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
status1 = serial_inp(up, UART_MSR) & 0xF0; status1 = serial_inp(up, UART_MSR) & 0xF0;
serial_outp(up, UART_MCR, save_mcr); serial_outp(up, UART_MCR, save_mcr);
if (status1 != 0x90) { if (status1 != 0x90) {
DEBUG_AUTOCONF("serial: ttyS%d: no UART loopback " DEBUG_AUTOCONF("LOOP test failed (%02x) ",
"failed\n", up->port.line); status1);
goto out; goto out;
} }
} }
serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */
serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */ /*
* We're pretty sure there's a port here. Lets find out what
* type of port it is. The IIR top two bits allows us to find
* out if its 8250 or 16450, 16550, 16550A or later. This
* determines what we test for next.
*
* We also initialise the EFR (if any) to zero for later. The
* EFR occupies the same register location as the FCR and IIR.
*/
serial_outp(up, UART_LCR, 0xBF);
serial_outp(up, UART_EFR, 0);
serial_outp(up, UART_LCR, 0); serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(up, UART_IIR) >> 6; scratch = serial_in(up, UART_IIR) >> 6;
DEBUG_AUTOCONF("iir=%d ", scratch);
switch (scratch) { switch (scratch) {
case 0: case 0:
up->port.type = PORT_16450; autoconfig_8250(up);
break; break;
case 1: case 1:
up->port.type = PORT_UNKNOWN; up->port.type = PORT_UNKNOWN;
break; break;
case 2: case 2:
up->port.type = PORT_16550; up->port.type = PORT_16550;
break; break;
case 3: case 3:
up->port.type = PORT_16550A; autoconfig_16550a(up);
break; break;
}
if (up->port.type == PORT_16550A) {
/* Check for Startech UART's */
serial_outp(up, UART_LCR, UART_LCR_DLAB);
if (serial_in(up, UART_EFR) == 0) {
up->port.type = PORT_16650;
} else {
serial_outp(up, UART_LCR, 0xBF);
if (serial_in(up, UART_EFR) == 0)
autoconfig_startech_uarts(up);
}
}
if (up->port.type == PORT_16550A) {
/* Check for TI 16750 */
serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB);
serial_outp(up, UART_FCR,
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
scratch = serial_in(up, UART_IIR) >> 5;
if (scratch == 7) {
/*
* If this is a 16750, and not a cheap UART
* clone, then it should only go into 64 byte
* mode if the UART_FCR7_64BYTE bit was set
* while UART_LCR_DLAB was latched.
*/
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_outp(up, UART_LCR, 0);
serial_outp(up, UART_FCR,
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
scratch = serial_in(up, UART_IIR) >> 5;
if (scratch == 6)
up->port.type = PORT_16750;
}
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
} }
#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) #if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
/* /*
* Only probe for RSA ports if we got the region. * Only probe for RSA ports if we got the region.
...@@ -588,17 +644,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) ...@@ -588,17 +644,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
} }
#endif #endif
serial_outp(up, UART_LCR, save_lcr); serial_outp(up, UART_LCR, save_lcr);
if (up->port.type == PORT_16450) {
scratch = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, 0xa5);
status1 = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, 0x5a);
status2 = serial_in(up, UART_SCR);
serial_outp(up, UART_SCR, scratch);
if ((status1 != 0xa5) || (status2 != 0x5a))
up->port.type = PORT_8250;
}
up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
...@@ -630,6 +675,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) ...@@ -630,6 +675,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
"serial_rsa"); "serial_rsa");
} }
#endif #endif
DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
} }
static void autoconfig_irq(struct uart_8250_port *up) static void autoconfig_irq(struct uart_8250_port *up)
......
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