diff options
author | Ben Guthro <benjamin.guthro@citrix.com> | 2013-04-24 11:41:53 +0200 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-04-24 11:41:53 +0200 |
commit | 6e96c186d23873597896051b043cfeb119c4a7d5 (patch) | |
tree | 28cf07f1300be3a5a56d0764f3cbaf9927a6cf74 /xen/drivers | |
parent | 3f28d0077788e7f8cd3ee25b023a4225d7e26e87 (diff) | |
download | xen-6e96c186d23873597896051b043cfeb119c4a7d5.tar.gz xen-6e96c186d23873597896051b043cfeb119c4a7d5.tar.bz2 xen-6e96c186d23873597896051b043cfeb119c4a7d5.zip |
ns16550: delay resume until dom0 ACPI has a chance to run
Check for ioport access, before fully resuming operation, to avoid
spinning in __ns16550_poll when reading the LSR register returns 0xFF
on failing ioport access.
On some systems (like Lenovo T410, and some HP machines of similar vintage)
there is a SuperIO card that provides this legacy ioport on the LPC bus.
In this case, we need to wait for dom0's ACPI processing to run the proper
AML to re-initialize the chip, before we can use the card again.
This may cause a small amount of garbage to be written to the serial log
while we wait patiently for that AML to be executed.
This implementation limits the number of retries, to avoid a situation
where we keep trying over and over again, in the case of some other failure
on the ioport.
Signed-Off-By: Ben Guthro <benjamin.guthro@citrix.com>
Acked-by: Keir Fraser <keir@xen.org>
Diffstat (limited to 'xen/drivers')
-rw-r--r-- | xen/drivers/char/ns16550.c | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c index a91743e2ac..e0c87bb5f5 100644 --- a/xen/drivers/char/ns16550.c +++ b/xen/drivers/char/ns16550.c @@ -45,6 +45,7 @@ static struct ns16550 { struct irqaction irqaction; /* UART with no IRQ line: periodically-polled I/O. */ struct timer timer; + struct timer resume_timer; unsigned int timeout_ms; bool_t intr_works; /* PCI card parameters. */ @@ -123,6 +124,10 @@ static struct ns16550 { /* Frequency of external clock source. This definition assumes PC platform. */ #define UART_CLOCK_HZ 1843200 +/* Resume retry settings */ +#define RESUME_DELAY MILLISECS(10) +#define RESUME_RETRIES 100 + static char ns_read_reg(struct ns16550 *uart, int reg) { if ( uart->remapped_io_base == NULL ) @@ -351,7 +356,7 @@ static void ns16550_suspend(struct serial_port *port) uart->ps_bdf[2], PCI_COMMAND); } -static void ns16550_resume(struct serial_port *port) +static void _ns16550_resume(struct serial_port *port) { struct ns16550 *uart = port->uart; @@ -367,6 +372,54 @@ static void ns16550_resume(struct serial_port *port) ns16550_setup_postirq(port->uart); } +static int ns16550_ioport_invalid(struct ns16550 *uart) +{ + return ((((unsigned char)ns_read_reg(uart, LSR)) == 0xff) && + (((unsigned char)ns_read_reg(uart, MCR)) == 0xff) && + (((unsigned char)ns_read_reg(uart, IER)) == 0xff) && + (((unsigned char)ns_read_reg(uart, IIR)) == 0xff) && + (((unsigned char)ns_read_reg(uart, LCR)) == 0xff)); +} + +static int delayed_resume_tries; +static void ns16550_delayed_resume(void *data) +{ + struct serial_port *port = data; + struct ns16550 *uart = port->uart; + + if ( ns16550_ioport_invalid(port->uart) && delayed_resume_tries-- ) + set_timer(&uart->resume_timer, NOW() + RESUME_DELAY); + else + _ns16550_resume(port); +} + +static void ns16550_resume(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + + /* + * Check for ioport access, before fully resuming operation. + * On some systems, there is a SuperIO card that provides + * this legacy ioport on the LPC bus. + * + * We need to wait for dom0's ACPI processing to run the proper + * AML to re-initialize the chip, before we can use the card again. + * + * This may cause a small amount of garbage to be written + * to the serial log while we wait patiently for that AML to + * be executed. However, this is preferable to spinning in an + * infinite loop, as seen on a Lenovo T430, when serial was enabled. + */ + if ( ns16550_ioport_invalid(uart) ) + { + delayed_resume_tries = RESUME_RETRIES; + init_timer(&uart->resume_timer, ns16550_delayed_resume, port, 0); + set_timer(&uart->resume_timer, NOW() + RESUME_DELAY); + } + else + _ns16550_resume(port); +} + #ifdef CONFIG_X86 static void __init ns16550_endboot(struct serial_port *port) { |