From: Kevin Monroe <kmonroe@redhat.com> Date: Fri, 21 Aug 2009 18:30:26 -0400 Subject: [x86] pnpacpi: fix serial ports on IBM Point-of-Sale HW Message-id: 20090821222658.21144.2154.sendpatchset@squad5-lp1.lab.bos.redhat.com O-Subject: [PATCH RHEL5.5 BZ506799] Alter pnpacpi code to fix serial ports on IBM POS hardware Bugzilla: 506799 RH-Acked-by: Matthew Garrett <mjg@redhat.com> RHBZ#: ====== https://bugzilla.redhat.com/show_bug.cgi?id=506799 Description: ============ Reading/writing with a loopback serial device on IBM POS 4838-310 hardware does not work without the pnpacpi=off boot option. An upstream ACPI fix was made that addresses this issue. Description of upstream commit: Use mp_irqs[] to get PNP device's interrupt polarity and trigger. There are two reasons to do this: 1. BIOS bug for PNP interrupt 2. BIOS explictly does override mp_irqs[] should cover all the cases. RHEL Version Found: =================== RHEL 5.4 kABI Status: ============ No symbols were harmed. Brew: ===== Built on all platforms. http://brewweb.devel.redhat.com/brew/taskinfo?taskID=1939926 Upstream Status: ================ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=61fd47e0c84764f49b4e52bfd8170fac52636f00 Test Status: ============ Testing was done by connecting a serial loopback device, reading from one tty (cat /dev/ttyS0) and writing to another (echo foo > /dev/ttyS1). Without this patch, the echo blocks for a while and then returns with no console output seen. After this patch is applied, the test runs as expected ("foo" is output to the console). =============================================================== Kevin Monroe 978-392-3183 ext 23183 IBM on-site partner Proposed Patch: =============== diff --git a/arch/i386/kernel/io_apic-xen.c b/arch/i386/kernel/io_apic-xen.c index dc20fcc..c479de3 100644 --- a/arch/i386/kernel/io_apic-xen.c +++ b/arch/i386/kernel/io_apic-xen.c @@ -963,7 +963,7 @@ static int EISA_ELCR(unsigned int irq) #define default_NEC98_trigger(idx) (0) #define default_NEC98_polarity(idx) (0) -static int __init MPBIOS_polarity(int idx) +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; int polarity; @@ -2768,4 +2768,23 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a return 0; } +int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) +{ + int i; + + if (skip_ioapic_setup) + return -1; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == mp_INT && + mp_irqs[i].mpc_srcbusirq == bus_irq) + break; + if (i >= mp_irq_entries) + return -1; + + *trigger = irq_trigger(i); + *polarity = irq_polarity(i); + return 0; +} + #endif /* CONFIG_ACPI */ diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 9a21455..84889d1 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -915,7 +915,7 @@ static int EISA_ELCR(unsigned int irq) #define default_NEC98_trigger(idx) (0) #define default_NEC98_polarity(idx) (0) -static int __init MPBIOS_polarity(int idx) +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; int polarity; @@ -2711,4 +2711,23 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a return 0; } +int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) +{ + int i; + + if (skip_ioapic_setup) + return -1; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == mp_INT && + mp_irqs[i].mpc_srcbusirq == bus_irq) + break; + if (i >= mp_irq_entries) + return -1; + + *trigger = irq_trigger(i); + *polarity = irq_polarity(i); + return 0; +} + #endif /* CONFIG_ACPI */ diff --git a/arch/x86_64/kernel/io_apic-xen.c b/arch/x86_64/kernel/io_apic-xen.c index bc894fa..b47bcee 100644 --- a/arch/x86_64/kernel/io_apic-xen.c +++ b/arch/x86_64/kernel/io_apic-xen.c @@ -601,7 +601,7 @@ static int EISA_ELCR(unsigned int irq) #define default_MCA_trigger(idx) (1) #define default_MCA_polarity(idx) (0) -static int __init MPBIOS_polarity(int idx) +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; int polarity; @@ -2229,6 +2229,25 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a return 0; } +int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) +{ + int i; + + if (skip_ioapic_setup) + return -1; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == mp_INT && + mp_irqs[i].mpc_srcbusirq == bus_irq) + break; + if (i >= mp_irq_entries) + return -1; + + *trigger = irq_trigger(i); + *polarity = irq_polarity(i); + return 0; +} + #endif /* CONFIG_ACPI */ diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index e4ddc3c..3caf95d 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c @@ -553,7 +553,7 @@ static int EISA_ELCR(unsigned int irq) #define default_MCA_trigger(idx) (1) #define default_MCA_polarity(idx) (0) -static int __init MPBIOS_polarity(int idx) +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; int polarity; @@ -2294,6 +2294,25 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p return 0; } +int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) +{ + int i; + + if (skip_ioapic_setup) + return -1; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == mp_INT && + mp_irqs[i].mpc_srcbusirq == bus_irq) + break; + if (i >= mp_irq_entries) + return -1; + + *trigger = irq_trigger(i); + *polarity = irq_polarity(i); + return 0; +} + #endif /* CONFIG_ACPI */ diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index dc79b0a..720c13a 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -78,6 +78,7 @@ pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, u32 gsi, { int i = 0; int irq; + int p, t; if (!valid_IRQ(gsi)) return; @@ -88,6 +89,23 @@ pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, u32 gsi, if (i >= PNP_MAX_IRQ) return; + /* + * in IO-APIC mode, use overrided attribute. Two reasons: + * 1. BIOS bug in DSDT + * 2. BIOS uses IO-APIC mode Interrupt Source Override + */ + if (!acpi_get_override_irq(gsi, &t, &p)) { + t = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; + p = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; + + if (triggering != t || polarity != p) { + pnp_warn("IRQ %d override to %s, %s", + gsi, t ? "edge":"level", p ? "low":"high"); + triggering = t; + polarity = p; + } + } + res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag irq = acpi_register_gsi(gsi, triggering, polarity); if (irq < 0) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 41df59f..0f5676d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -450,6 +450,11 @@ extern unsigned long acpi_video_flags; int acpi_register_gsi (u32 gsi, int triggering, int polarity); int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); +#ifdef CONFIG_X86_IO_APIC +extern int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity); +#else +#define acpi_get_override_irq(bus, trigger, polarity) (-1) +#endif /* * This function undoes the effect of one call to acpi_register_gsi(). * If this matches the last registration, any IRQ resources for gsi