Bring in YONETANI Tomokazu's acpi-update-2.patch (27-May-2004), a major
[dragonfly.git] / sys / platform / pc32 / acpica5 / madt.c
index 4433470..013411a 100644 (file)
@@ -26,8 +26,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/i386/acpica/madt.c,v 1.10 2004/01/26 19:34:24 jhb Exp $
- * $DragonFly: src/sys/platform/pc32/acpica5/madt.c,v 1.1 2004/02/21 06:48:05 dillon Exp $
+ * $FreeBSD: src/sys/i386/acpica/madt.c,v 1.15 2004/05/11 20:06:32 jhb Exp $
+ * $DragonFly: src/sys/platform/pc32/acpica5/madt.c,v 1.2 2004/06/27 08:52:45 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -51,6 +51,7 @@
 #include <machine/globaldata.h>
 
 #include "acpi.h"
+#include "actables.h"
 #include "acpivar.h"
 #include <bus/pci/pcivar.h>
 
@@ -76,14 +77,15 @@ struct lapic_info {
        u_int la_apic_id:8;
 } lapics[NLAPICS + 1];
 
+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);
@@ -164,6 +166,7 @@ madt_map_table(vm_paddr_t pa, int offset, const char *sig)
 {
        ACPI_TABLE_HEADER *header;
        vm_offset_t length;
+       void *table;
 
        header = madt_map(pa, offset, sizeof(ACPI_TABLE_HEADER));
        if (strncmp(header->Signature, sig, 4) != 0) {
@@ -172,7 +175,14 @@ madt_map_table(vm_paddr_t pa, int offset, const char *sig)
        }
        length = header->Length;
        madt_unmap(header, sizeof(ACPI_TABLE_HEADER));
-       return (madt_map(pa, offset, length));
+       table = madt_map(pa, offset, length);
+       if (ACPI_FAILURE(AcpiTbVerifyTableChecksum(table))) {
+               if (bootverbose)
+                       printf("MADT: Failed checksum for table %s\n", sig);
+               madt_unmap(table, length);
+               return (NULL);
+       }
+       return (table);
 }
 
 static void
@@ -224,6 +234,16 @@ madt_probe(void)
         * Page 0 is used to map in the headers of candidate ACPI tables.
         */
        if (rsdp->Revision >= 2) {
+               /*
+                * AcpiOsGetRootPointer only verifies the checksum for
+                * the version 1.0 portion of the RSDP.  Version 2.0 has
+                * an additional checksum that we verify first.
+                */
+               if (AcpiTbChecksum(rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0) {
+                       if (bootverbose)
+                               printf("MADT: RSDP failed extended checksum\n");
+                       return (ENXIO);
+               }
                xsdt = madt_map_table(rsdp->XsdtPhysicalAddress, 1, XSDT_SIG);
                if (xsdt == NULL) {
                        if (bootverbose)
@@ -260,6 +280,16 @@ madt_probe(void)
                printf("MADT: Found table at 0x%jx\n",
                    (uintmax_t)madt_physaddr);
 
+       /*
+        * Verify that we can map the full table and that its checksum is
+        * correct, etc.
+        */
+       madt = madt_map_table(madt_physaddr, 0, APIC_SIG);
+       if (madt == NULL)
+               return (ENXIO);
+       madt_unmap_table(madt);
+       madt = NULL;
+
        return (0);
 }
 
@@ -282,7 +312,6 @@ madt_probe_table(vm_paddr_t address)
                printf("Table '%.4s' at 0x%jx\n", table->Signature,
                    (uintmax_t)address);
 
-       /* XXX: Verify checksum? */
        if (strncmp(table->Signature, APIC_SIG, 4) != 0) {
                madt_unmap(table, sizeof(ACPI_TABLE_HEADER));
                return (0);
@@ -334,6 +363,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. */
@@ -346,11 +377,30 @@ madt_setup_io(void)
        }
                    
        /* First, we run through adding I/O APIC's. */
+       if (madt->PCATCompat)
+               ioapic_enable_mixed_mode();
        madt_walk_table(madt_parse_apics, NULL);
 
        /* 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)
@@ -454,35 +504,44 @@ 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.
+ * Determine properties of an interrupt source.  Note that for ACPI these
+ * functions are only used for ISA interrupts, so we assume ISA bus 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");
        }
@@ -540,7 +599,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",
@@ -554,18 +615,46 @@ madt_parse_interrupt_override(MADT_INTERRUPT_OVERRIDE *intr)
        }
 
        /*
-        * If the SCI is remapped to a non-ISA global interrupt,
-        * force it to level trigger and active-lo polarity.
+        * 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, also force it to use level/lo. 
+        * 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 (intr->Interrupt > 15 || (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,
@@ -585,18 +674,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);
 }
 
 /*
@@ -617,10 +698,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));
 }
 
 /*
@@ -646,10 +727,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));
 }
 
 /*