- Change the APIC code to mostly use the recently added intr_trigger

and intr_polarity enums for passing around interrupt trigger modes and
  polarity rather than using the magic numbers 0 for level/low and 1 for
  edge/high.
- Convert the mptable parsing code to use the new ELCR wrapper code rather
  than reading the ELCR directly.  Also, use the ELCR settings to control
  both the trigger and polarity of EISA IRQs instead of just the trigger
  mode.
- Rework the MADT's handling of the ACPI SCI again:
  - If no override entry for the SCI exists at all, use level/low trigger
    instead of the default edge/high used for ISA IRQs.
  - For the ACPI SCI, use level/low values for conforming trigger and
    polarity rather than the edge/high values we use for all other ISA
    IRQs.
  - Rework the tunables available to override the MADT.  The
    hw.acpi.force_sci_lo tunable is no longer supported.  Instead, there
    are now two tunables that can independently override the trigger mode
    and/or polarity of the SCI.  The hw.acpi.sci.trigger tunable can be
    set to either "edge" or "level", and the hw.acpi.sci.polarity tunable
    can be set to either "high" or "low".  To simulate hw.acpi.force_sci_lo,
    set hw.acpi.sci.trigger to "level" and hw.acpi.sci.polarity to "low".
    If you are having problems with ACPI either causing an interrupt storm
    or not working at all (e.g., the power button doesn't turn invoke a
    shutdown -p now), you can try tweaking these two tunables to find the
    combination that works.
This commit is contained in:
jhb 2004-05-04 20:39:24 +00:00
parent 4bc7c8e398
commit e077495344
5 changed files with 140 additions and 94 deletions

View File

@ -69,16 +69,15 @@ struct lapic_info {
u_int la_apic_id:8;
} lapics[NLAPICS + 1];
static int force_sci_lo;
TUNABLE_INT("hw.acpi.force_sci_lo", &force_sci_lo);
static int madt_found_sci_override;
static MULTIPLE_APIC_TABLE *madt;
static vm_paddr_t madt_physaddr;
static vm_offset_t madt_length;
MALLOC_DEFINE(M_MADT, "MADT Table", "ACPI MADT Table Items");
static u_char interrupt_polarity(UINT16 Polarity);
static u_char interrupt_trigger(UINT16 TriggerMode);
static enum intr_polarity interrupt_polarity(UINT16 Polarity, UINT8 Source);
static enum intr_trigger interrupt_trigger(UINT16 TriggerMode, UINT8 Source);
static int madt_find_cpu(u_int acpi_id, u_int *apic_id);
static int madt_find_interrupt(int intr, void **apic, u_int *pin);
static void *madt_map(vm_paddr_t pa, int offset, vm_offset_t length);
@ -327,6 +326,8 @@ madt_setup_local(void)
static int
madt_setup_io(void)
{
void *ioapic;
u_int pin;
int i;
/* Try to initialize ACPI so that we can access the FADT. */
@ -344,6 +345,23 @@ madt_setup_io(void)
/* Second, we run through the table tweaking interrupt sources. */
madt_walk_table(madt_parse_ints, NULL);
/*
* If there was not an explicit override entry for the SCI,
* force it to use level trigger and active-low polarity.
*/
if (!madt_found_sci_override) {
if (madt_find_interrupt(AcpiGbl_FADT->SciInt, &ioapic, &pin)
!= 0)
printf("MADT: Could not find APIC for SCI IRQ %d\n",
AcpiGbl_FADT->SciInt);
else {
printf(
"MADT: Forcing active-low polarity and level trigger for SCI\n");
ioapic_set_polarity(ioapic, pin, INTR_POLARITY_LOW);
ioapic_set_triggermode(ioapic, pin, INTR_TRIGGER_LEVEL);
}
}
/* Third, we register all the I/O APIC's. */
for (i = 0; i < NIOAPICS; i++)
if (ioapics[i].io_apic != NULL)
@ -449,33 +467,42 @@ madt_parse_apics(APIC_HEADER *entry, void *arg __unused)
/*
* Determine properties of an interrupt source. Note that for ACPI,
* these are only used for ISA interrupts, so we assume ISA bus values
* (Active Hi, Edge Triggered) for conforming values.
* (Active Hi, Edge Triggered) for conforming values except for the ACPI
* SCI for which we use Active Lo, Level Triggered..
*/
static u_char
interrupt_polarity(UINT16 Polarity)
static enum intr_polarity
interrupt_polarity(UINT16 Polarity, UINT8 Source)
{
switch (Polarity) {
case POLARITY_CONFORMS:
if (Source == AcpiGbl_FADT->SciInt)
return (INTR_POLARITY_LOW);
else
return (INTR_POLARITY_HIGH);
case POLARITY_ACTIVE_HIGH:
return (1);
return (INTR_POLARITY_HIGH);
case POLARITY_ACTIVE_LOW:
return (0);
return (INTR_POLARITY_LOW);
default:
panic("Bogus Interrupt Polarity");
}
}
static u_char
interrupt_trigger(UINT16 TriggerMode)
static enum intr_trigger
interrupt_trigger(UINT16 TriggerMode, UINT8 Source)
{
switch (TriggerMode) {
case TRIGGER_CONFORMS:
if (Source == AcpiGbl_FADT->SciInt)
return (INTR_TRIGGER_LEVEL);
else
return (INTR_TRIGGER_EDGE);
case TRIGGER_EDGE:
return (1);
return (INTR_TRIGGER_EDGE);
case TRIGGER_LEVEL:
return (0);
return (INTR_TRIGGER_LEVEL);
default:
panic("Bogus Interrupt Trigger Mode");
}
@ -533,7 +560,9 @@ madt_parse_interrupt_override(MADT_INTERRUPT_OVERRIDE *intr)
{
void *new_ioapic, *old_ioapic;
u_int new_pin, old_pin;
int force_lo;
enum intr_trigger trig;
enum intr_polarity pol;
char buf[64];
if (bootverbose)
printf("MADT: intr override: source %u, irq %u\n",
@ -546,18 +575,47 @@ madt_parse_interrupt_override(MADT_INTERRUPT_OVERRIDE *intr)
return;
}
/*
* Lookup the appropriate trigger and polarity modes for this
* entry.
*/
trig = interrupt_trigger(intr->TriggerMode, intr->Source);
pol = interrupt_polarity(intr->Polarity, intr->Source);
/*
* If the SCI is identity mapped but has edge trigger and
* active-hi polarity or the force_sci_lo tunable is set,
* force it to use level/lo.
*/
force_lo = 0;
if (intr->Source == AcpiGbl_FADT->SciInt)
if (force_sci_lo || (intr->Interrupt == intr->Source &&
intr->TriggerMode == TRIGGER_EDGE &&
intr->Polarity == POLARITY_ACTIVE_HIGH))
force_lo = 1;
if (intr->Source == AcpiGbl_FADT->SciInt) {
madt_found_sci_override = 1;
if (getenv_string("hw.acpi.sci.trigger", buf, sizeof(buf))) {
if (tolower(buf[0]) == 'e')
trig = INTR_TRIGGER_EDGE;
else if (tolower(buf[0]) == 'l')
trig = INTR_TRIGGER_LEVEL;
else
panic(
"Invalid trigger %s: must be 'edge' or 'level'",
buf);
printf("MADT: Forcing SCI to %s trigger\n",
trig == INTR_TRIGGER_EDGE ? "edge" : "level");
}
if (getenv_string("hw.acpi.sci.polarity", buf, sizeof(buf))) {
if (tolower(buf[0]) == 'h')
pol = INTR_POLARITY_HIGH;
else if (tolower(buf[0]) == 'l')
pol = INTR_POLARITY_LOW;
else
panic(
"Invalid polarity %s: must be 'high' or 'low'",
buf);
printf("MADT: Forcing SCI to active %s polarity\n",
pol == INTR_POLARITY_HIGH ? "high" : "low");
}
}
/* Remap the IRQ if it is mapped to a different interrupt vector. */
if (intr->Source != intr->Interrupt) {
/*
* If the SCI is remapped to a non-ISA global interrupt,
@ -577,18 +635,10 @@ madt_parse_interrupt_override(MADT_INTERRUPT_OVERRIDE *intr)
intr->Source)
ioapic_disable_pin(old_ioapic, old_pin);
}
if (force_lo) {
printf(
"MADT: Forcing active-lo polarity and level trigger for IRQ %d\n",
intr->Source);
ioapic_set_polarity(new_ioapic, new_pin, 0);
ioapic_set_triggermode(new_ioapic, new_pin, 0);
} else {
ioapic_set_polarity(new_ioapic, new_pin,
interrupt_polarity(intr->Polarity));
ioapic_set_triggermode(new_ioapic, new_pin,
interrupt_trigger(intr->TriggerMode));
}
/* Program the polarity and trigger mode. */
ioapic_set_triggermode(new_ioapic, new_pin, trig);
ioapic_set_polarity(new_ioapic, new_pin, pol);
}
/*
@ -609,10 +659,10 @@ madt_parse_nmi(MADT_NMI_SOURCE *nmi)
ioapic_set_nmi(ioapic, pin);
if (nmi->TriggerMode != TRIGGER_CONFORMS)
ioapic_set_triggermode(ioapic, pin,
interrupt_trigger(nmi->TriggerMode));
interrupt_trigger(nmi->TriggerMode, 0));
if (nmi->Polarity != TRIGGER_CONFORMS)
ioapic_set_polarity(ioapic, pin,
interrupt_polarity(nmi->Polarity));
interrupt_polarity(nmi->Polarity, 0));
}
/*
@ -638,10 +688,10 @@ madt_parse_local_nmi(MADT_LOCAL_APIC_NMI *nmi)
lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI);
if (nmi->TriggerMode != TRIGGER_CONFORMS)
lapic_set_lvt_triggermode(apic_id, pin,
interrupt_trigger(nmi->TriggerMode));
interrupt_trigger(nmi->TriggerMode, 0));
if (nmi->Polarity != POLARITY_CONFORMS)
lapic_set_lvt_polarity(apic_id, pin,
interrupt_polarity(nmi->Polarity));
interrupt_polarity(nmi->Polarity, 0));
}
/*

View File

@ -525,36 +525,36 @@ ioapic_set_extint(void *cookie, u_int pin)
}
int
ioapic_set_polarity(void *cookie, u_int pin, char activehi)
ioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol)
{
struct ioapic *io;
io = (struct ioapic *)cookie;
if (pin >= io->io_numintr)
if (pin >= io->io_numintr || pol == INTR_POLARITY_CONFORM)
return (EINVAL);
if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
return (EINVAL);
io->io_pins[pin].io_activehi = activehi;
io->io_pins[pin].io_activehi = (pol == INTR_POLARITY_HIGH);
if (bootverbose)
printf("ioapic%u: intpin %d polarity: %s\n", io->io_id, pin,
activehi ? "active-hi" : "active-lo");
pol == INTR_POLARITY_HIGH ? "high" : "low");
return (0);
}
int
ioapic_set_triggermode(void *cookie, u_int pin, char edgetrigger)
ioapic_set_triggermode(void *cookie, u_int pin, enum intr_trigger trigger)
{
struct ioapic *io;
io = (struct ioapic *)cookie;
if (pin >= io->io_numintr)
if (pin >= io->io_numintr || trigger == INTR_TRIGGER_CONFORM)
return (EINVAL);
if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
return (EINVAL);
io->io_pins[pin].io_edgetrigger = edgetrigger;
io->io_pins[pin].io_edgetrigger = (trigger == INTR_TRIGGER_EDGE);
if (bootverbose)
printf("ioapic%u: intpin %d trigger: %s\n", io->io_id, pin,
edgetrigger ? "edge" : "level");
trigger == INTR_TRIGGER_EDGE ? "edge" : "level");
return (0);
}

View File

@ -423,50 +423,52 @@ lapic_set_lvt_mode(u_int apic_id, u_int pin, u_int32_t mode)
}
int
lapic_set_lvt_polarity(u_int apic_id, u_int pin, u_char activehi)
lapic_set_lvt_polarity(u_int apic_id, u_int pin, enum intr_polarity pol)
{
if (pin > LVT_MAX)
if (pin > LVT_MAX || pol == INTR_POLARITY_CONFORM)
return (EINVAL);
if (apic_id == APIC_ID_ALL) {
lvts[pin].lvt_activehi = activehi;
lvts[pin].lvt_activehi = (pol == INTR_POLARITY_HIGH);
if (bootverbose)
printf("lapic:");
} else {
KASSERT(lapics[apic_id].la_present,
("%s: missing APIC %u", __func__, apic_id));
lapics[apic_id].la_lvts[pin].lvt_active = 1;
lapics[apic_id].la_lvts[pin].lvt_activehi = activehi;
lapics[apic_id].la_lvts[pin].lvt_activehi =
(pol == INTR_POLARITY_HIGH);
if (bootverbose)
printf("lapic%u:", apic_id);
}
if (bootverbose)
printf(" LINT%u polarity: active-%s\n", pin,
activehi ? "hi" : "lo");
pol == INTR_POLARITY_HIGH ? "high" : "low");
return (0);
}
int
lapic_set_lvt_triggermode(u_int apic_id, u_int pin, u_char edgetrigger)
lapic_set_lvt_triggermode(u_int apic_id, u_int pin, enum intr_trigger trigger)
{
if (pin > LVT_MAX)
if (pin > LVT_MAX || trigger == INTR_TRIGGER_CONFORM)
return (EINVAL);
if (apic_id == APIC_ID_ALL) {
lvts[pin].lvt_edgetrigger = edgetrigger;
lvts[pin].lvt_edgetrigger = (trigger == INTR_TRIGGER_EDGE);
if (bootverbose)
printf("lapic:");
} else {
KASSERT(lapics[apic_id].la_present,
("%s: missing APIC %u", __func__, apic_id));
lapics[apic_id].la_lvts[pin].lvt_edgetrigger = edgetrigger;
lapics[apic_id].la_lvts[pin].lvt_edgetrigger =
(trigger == INTR_TRIGGER_EDGE);
lapics[apic_id].la_lvts[pin].lvt_active = 1;
if (bootverbose)
printf("lapic%u:", apic_id);
}
if (bootverbose)
printf(" LINT%u trigger: %s\n", pin,
edgetrigger ? "edge" : "level");
trigger == INTR_TRIGGER_EDGE ? "edge" : "level");
return (0);
}

View File

@ -48,10 +48,6 @@ __FBSDID("$FreeBSD$");
#include <dev/pci/pcivar.h>
/* EISA Edge/Level trigger control registers */
#define ELCR0 0x4d0 /* eisa irq 0-7 */
#define ELCR1 0x4d1 /* eisa irq 8-15 */
/* string defined by the Intel MP Spec as identifying the MP table */
#define MP_SIG 0x5f504d5f /* _MP_ */
@ -153,10 +149,11 @@ static int pci0 = -1;
MALLOC_DEFINE(M_MPTABLE, "MP Table", "MP Table Items");
static u_char conforming_polarity(u_char src_bus);
static u_char conforming_trigger(u_char src_bus, u_char src_bus_irq);
static u_char intentry_polarity(int_entry_ptr intr);
static u_char intentry_trigger(int_entry_ptr intr);
static enum intr_polarity conforming_polarity(u_char src_bus,
u_char src_bus_irq);
static enum intr_trigger conforming_trigger(u_char src_bus, u_char src_bus_irq);
static enum intr_polarity intentry_polarity(int_entry_ptr intr);
static enum intr_trigger intentry_trigger(int_entry_ptr intr);
static int lookup_bus_type(char *name);
static void mptable_count_items(void);
static void mptable_count_items_handler(u_char *entry, void *arg);
@ -544,19 +541,22 @@ mptable_parse_apics_and_busses(void)
/*
* Determine conforming polarity for a given bus type.
*/
static u_char
conforming_polarity(u_char src_bus)
static enum intr_polarity
conforming_polarity(u_char src_bus, u_char src_bus_irq)
{
KASSERT(src_bus <= mptable_maxbusid, ("bus id %d too large", src_bus));
switch (busses[src_bus].bus_type) {
case ISA:
case EISA:
/* Active Hi */
return (1);
return (INTR_POLARITY_HIGH);
case PCI:
/* Active Lo */
return (0);
return (INTR_POLARITY_LOW);
case EISA:
KASSERT(src_bus_irq < 16, ("Invalid EISA IRQ %d", src_bus_irq));
if (elcr_read_trigger(src_bus_irq) == INTR_TRIGGER_LEVEL)
return (INTR_POLARITY_LOW);
else
return (INTR_POLARITY_HIGH);
default:
panic("%s: unknown bus type %d", __func__,
busses[src_bus].bus_type);
@ -566,52 +566,43 @@ conforming_polarity(u_char src_bus)
/*
* Determine conforming trigger for a given bus type.
*/
static u_char
static enum intr_trigger
conforming_trigger(u_char src_bus, u_char src_bus_irq)
{
static int eisa_int_control = -1;
KASSERT(src_bus <= mptable_maxbusid, ("bus id %d too large", src_bus));
switch (busses[src_bus].bus_type) {
case ISA:
/* Edge Triggered */
return (1);
return (INTR_TRIGGER_EDGE);
case PCI:
/* Level Triggered */
return (0);
return (INTR_TRIGGER_LEVEL);
case EISA:
KASSERT(src_bus_irq < 16, ("Invalid EISA IRQ %d", src_bus_irq));
if (eisa_int_control == -1)
eisa_int_control = inb(ELCR1) << 8 | inb(ELCR0);
if (eisa_int_control & (1 << src_bus_irq))
/* Level Triggered */
return (0);
else
/* Edge Triggered */
return (1);
return (elcr_read_trigger(src_bus_irq));
default:
panic("%s: unknown bus type %d", __func__,
busses[src_bus].bus_type);
}
}
static u_char
static enum intr_polarity
intentry_polarity(int_entry_ptr intr)
{
switch (intr->int_flags & INTENTRY_FLAGS_POLARITY) {
case INTENTRY_FLAGS_POLARITY_CONFORM:
return (conforming_polarity(intr->src_bus_id));
return (conforming_polarity(intr->src_bus_id,
intr->src_bus_irq));
case INTENTRY_FLAGS_POLARITY_ACTIVEHI:
return (1);
return (INTR_POLARITY_HIGH);
case INTENTRY_FLAGS_POLARITY_ACTIVELO:
return (0);
return (INTR_POLARITY_LOW);
default:
panic("Bogus interrupt flags");
}
}
static u_char
static enum intr_trigger
intentry_trigger(int_entry_ptr intr)
{
@ -620,9 +611,9 @@ intentry_trigger(int_entry_ptr intr)
return (conforming_trigger(intr->src_bus_id,
intr->src_bus_irq));
case INTENTRY_FLAGS_TRIGGER_EDGE:
return (1);
return (INTR_TRIGGER_EDGE);
case INTENTRY_FLAGS_TRIGGER_LEVEL:
return (0);
return (INTR_TRIGGER_LEVEL);
default:
panic("Bogus interrupt flags");
}

View File

@ -142,8 +142,9 @@ void ioapic_register(void *cookie);
int ioapic_remap_vector(void *cookie, u_int pin, int vector);
int ioapic_set_extint(void *cookie, u_int pin);
int ioapic_set_nmi(void *cookie, u_int pin);
int ioapic_set_polarity(void *cookie, u_int pin, char activehi);
int ioapic_set_triggermode(void *cookie, u_int pin, char edgetrigger);
int ioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol);
int ioapic_set_triggermode(void *cookie, u_int pin,
enum intr_trigger trigger);
int ioapic_set_smi(void *cookie, u_int pin);
void lapic_create(u_int apic_id, int boot_cpu);
void lapic_disable(void);
@ -160,8 +161,10 @@ void lapic_handle_intr(struct intrframe frame);
void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id);
int lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked);
int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode);
int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, u_char activehi);
int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, u_char edgetrigger);
int lapic_set_lvt_polarity(u_int apic_id, u_int lvt,
enum intr_polarity pol);
int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt,
enum intr_trigger trigger);
void lapic_setup(void);
#endif /* !LOCORE */