acpi/fadt: Make sure that SCI IRQ is valid
[dragonfly.git] / sys / platform / pc64 / acpica5 / acpi_fadt.c
index b1d86bd..6918f25 100644 (file)
 
 #include <sys/param.h>
 #include <sys/bus.h>
+#include <sys/interrupt.h>
 #include <sys/kernel.h>
+#include <sys/machintr.h>
 #include <sys/systm.h>
-
-#include <machine/pmap.h>
-#include <machine/smp.h>
-#include <machine/md_var.h>
-#include <machine/specialreg.h>
-#include <machine_base/apic/mpapic.h>
+#include <sys/thread2.h>
 
 #include "acpi_sdt.h"
 #include "acpi_sdt_var.h"
+#include "acpi_sci_var.h"
 
 #define FADT_VPRINTF(fmt, arg...) \
 do { \
@@ -68,13 +66,37 @@ struct acpi_fadt {
        /* More ... */
 } __packed;
 
+struct acpi_sci_mode {
+       enum intr_trigger       sci_trig;
+       enum intr_polarity      sci_pola;
+};
+
 static int                     acpi_sci_irq = -1;
+static enum intr_trigger       acpi_sci_trig = INTR_TRIGGER_CONFORM;
+static enum intr_polarity      acpi_sci_pola = INTR_POLARITY_CONFORM;
+
+static const struct acpi_sci_mode acpi_sci_modes[] = {
+       /*
+        * NOTE: Order is critical
+        */
+       { INTR_TRIGGER_LEVEL,   INTR_POLARITY_LOW },
+       { INTR_TRIGGER_LEVEL,   INTR_POLARITY_HIGH },
+       { INTR_TRIGGER_EDGE,    INTR_POLARITY_HIGH },
+       { INTR_TRIGGER_EDGE,    INTR_POLARITY_LOW },
+
+       /* Required last entry */
+       { INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM }
+};
 
 static void
 fadt_probe(void)
 {
        struct acpi_fadt *fadt;
        vm_paddr_t fadt_paddr;
+       enum intr_trigger trig;
+       enum intr_polarity pola;
+       int enabled = 1;
+       char *env;
 
        fadt_paddr = sdt_search(ACPI_FADT_SIG);
        if (fadt_paddr == 0) {
@@ -100,10 +122,147 @@ fadt_probe(void)
                goto back;
        }
 
+       kgetenv_int("hw.acpi.sci.enabled", &enabled);
+       if (!enabled)
+               goto back;
+
        acpi_sci_irq = fadt->fadt_sci_int;
-       kprintf("ACPI FADT: SCI irq %d\n", acpi_sci_irq);
 
+       env = kgetenv("hw.acpi.sci.trigger");
+       if (env == NULL)
+               goto back;
+
+       trig = INTR_TRIGGER_CONFORM;
+       if (strcmp(env, "edge") == 0)
+               trig = INTR_TRIGGER_EDGE;
+       else if (strcmp(env, "level") == 0)
+               trig = INTR_TRIGGER_LEVEL;
+
+       kfreeenv(env);
+
+       if (trig == INTR_TRIGGER_CONFORM)
+               goto back;
+
+       env = kgetenv("hw.acpi.sci.polarity");
+       if (env == NULL)
+               goto back;
+
+       pola = INTR_POLARITY_CONFORM;
+       if (strcmp(env, "high") == 0)
+               pola = INTR_POLARITY_HIGH;
+       else if (strcmp(env, "low") == 0)
+               pola = INTR_POLARITY_LOW;
+
+       kfreeenv(env);
+
+       if (pola == INTR_POLARITY_CONFORM)
+               goto back;
+
+       acpi_sci_trig = trig;
+       acpi_sci_pola = pola;
 back:
+       if (acpi_sci_irq >= 0) {
+               FADT_VPRINTF("SCI irq %d, %s/%s\n", acpi_sci_irq,
+                            intr_str_trigger(acpi_sci_trig),
+                            intr_str_polarity(acpi_sci_pola));
+       } else {
+               FADT_VPRINTF("SCI is disabled\n");
+       }
        sdt_sdth_unmap(&fadt->fadt_hdr);
 }
 SYSINIT(fadt_probe, SI_BOOT2_PRESMP, SI_ORDER_SECOND, fadt_probe, 0);
+
+static void
+acpi_sci_dummy_intr(void *dummy __unused, void *frame __unused)
+{
+}
+
+void
+acpi_sci_config(void)
+{
+       const struct acpi_sci_mode *mode;
+
+       KKASSERT(mycpuid == 0);
+
+       if (machintr_legacy_intr_find(acpi_sci_irq,
+           INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM) < 0) {
+               kprintf("ACPI FADT: SCI irq %d is invalid, disable\n",
+                   acpi_sci_irq);
+               acpi_sci_irq = -1;
+               return;
+       }
+
+       if (acpi_sci_irq < 0)
+               return;
+
+       if (acpi_sci_trig != INTR_TRIGGER_CONFORM) {
+               KKASSERT(acpi_sci_pola != INTR_POLARITY_CONFORM);
+               machintr_legacy_intr_config(acpi_sci_irq,
+                   acpi_sci_trig, acpi_sci_pola);
+               return;
+       }
+
+       kprintf("ACPI FADT: SCI testing interrupt mode ...\n");
+       for (mode = acpi_sci_modes; mode->sci_trig != INTR_TRIGGER_CONFORM;
+            ++mode) {
+               void *sci_desc;
+               long last_cnt;
+
+               FADT_VPRINTF("SCI testing %s/%s\n",
+                   intr_str_trigger(mode->sci_trig),
+                   intr_str_polarity(mode->sci_pola));
+
+               last_cnt = get_interrupt_counter(acpi_sci_irq, 0);
+
+               machintr_legacy_intr_config(acpi_sci_irq,
+                   mode->sci_trig, mode->sci_pola);
+
+               sci_desc = register_int(acpi_sci_irq,
+                   acpi_sci_dummy_intr, NULL, "sci", NULL,
+                   INTR_EXCL | INTR_CLOCK |
+                   INTR_NOPOLL | INTR_MPSAFE | INTR_NOENTROPY, 0);
+
+               DELAY(100 * 1000);
+
+               unregister_int(sci_desc, 0);
+
+               if (get_interrupt_counter(acpi_sci_irq, 0) - last_cnt < 20) {
+                       acpi_sci_trig = mode->sci_trig;
+                       acpi_sci_pola = mode->sci_pola;
+
+                       kprintf("ACPI FADT: SCI select %s/%s\n",
+                           intr_str_trigger(acpi_sci_trig),
+                           intr_str_polarity(acpi_sci_pola));
+                       return;
+               }
+       }
+
+       kprintf("ACPI FADT: no suitable interrupt mode for SCI, disable\n");
+       acpi_sci_irq = -1;
+}
+
+int
+acpi_sci_enabled(void)
+{
+       if (acpi_sci_irq >= 0)
+               return 1;
+       else
+               return 0;
+}
+
+int
+acpi_sci_pci_shariable(void)
+{
+       if (acpi_sci_irq >= 0 &&
+           acpi_sci_trig == INTR_TRIGGER_LEVEL &&
+           acpi_sci_pola == INTR_POLARITY_LOW)
+               return 1;
+       else
+               return 0;
+}
+
+int
+acpi_sci_irqno(void)
+{
+       return acpi_sci_irq;
+}