15db2f26eSSascha Wildner /*
25db2f26eSSascha Wildner * Copyright (c) 2009 The DragonFly Project. All rights reserved.
35db2f26eSSascha Wildner *
45db2f26eSSascha Wildner * This code is derived from software contributed to The DragonFly Project
55db2f26eSSascha Wildner * by Sepherosa Ziehau <sepherosa@gmail.com>
65db2f26eSSascha Wildner *
75db2f26eSSascha Wildner * Redistribution and use in source and binary forms, with or without
85db2f26eSSascha Wildner * modification, are permitted provided that the following conditions
95db2f26eSSascha Wildner * are met:
105db2f26eSSascha Wildner *
115db2f26eSSascha Wildner * 1. Redistributions of source code must retain the above copyright
125db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer.
135db2f26eSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
145db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer in
155db2f26eSSascha Wildner * the documentation and/or other materials provided with the
165db2f26eSSascha Wildner * distribution.
175db2f26eSSascha Wildner * 3. Neither the name of The DragonFly Project nor the names of its
185db2f26eSSascha Wildner * contributors may be used to endorse or promote products derived
195db2f26eSSascha Wildner * from this software without specific, prior written permission.
205db2f26eSSascha Wildner *
215db2f26eSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
225db2f26eSSascha Wildner * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
235db2f26eSSascha Wildner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
245db2f26eSSascha Wildner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
255db2f26eSSascha Wildner * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
265db2f26eSSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
275db2f26eSSascha Wildner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
285db2f26eSSascha Wildner * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
295db2f26eSSascha Wildner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
305db2f26eSSascha Wildner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
315db2f26eSSascha Wildner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
325db2f26eSSascha Wildner * SUCH DAMAGE.
335db2f26eSSascha Wildner */
345db2f26eSSascha Wildner
355db2f26eSSascha Wildner #include <sys/param.h>
365db2f26eSSascha Wildner #include <sys/bus.h>
375db2f26eSSascha Wildner #include <sys/interrupt.h>
385db2f26eSSascha Wildner #include <sys/kernel.h>
395db2f26eSSascha Wildner #include <sys/machintr.h>
405db2f26eSSascha Wildner #include <sys/systm.h>
415db2f26eSSascha Wildner
428d3ef488SSascha Wildner #include <contrib/dev/acpica/source/include/acpi.h>
438d3ef488SSascha Wildner
445db2f26eSSascha Wildner #include "acpi_sdt_var.h"
455db2f26eSSascha Wildner #include "acpi_sci_var.h"
465db2f26eSSascha Wildner
475db2f26eSSascha Wildner #define FADT_VPRINTF(fmt, arg...) \
485db2f26eSSascha Wildner do { \
495db2f26eSSascha Wildner if (bootverbose) \
505db2f26eSSascha Wildner kprintf("ACPI FADT: " fmt , ##arg); \
515db2f26eSSascha Wildner } while (0)
525db2f26eSSascha Wildner
535db2f26eSSascha Wildner struct acpi_sci_mode {
545db2f26eSSascha Wildner enum intr_trigger sci_trig;
555db2f26eSSascha Wildner enum intr_polarity sci_pola;
565db2f26eSSascha Wildner };
575db2f26eSSascha Wildner
585db2f26eSSascha Wildner static int acpi_sci_irq = -1;
595db2f26eSSascha Wildner static enum intr_trigger acpi_sci_trig = INTR_TRIGGER_CONFORM;
605db2f26eSSascha Wildner static enum intr_polarity acpi_sci_pola = INTR_POLARITY_CONFORM;
615db2f26eSSascha Wildner static const struct acpi_sci_mode *acpi_sci_mode1 = NULL;
625db2f26eSSascha Wildner
635db2f26eSSascha Wildner static const struct acpi_sci_mode acpi_sci_modes[] = {
645db2f26eSSascha Wildner /*
655db2f26eSSascha Wildner * NOTE: Order is critical
665db2f26eSSascha Wildner */
675db2f26eSSascha Wildner { INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW },
685db2f26eSSascha Wildner { INTR_TRIGGER_LEVEL, INTR_POLARITY_HIGH },
695db2f26eSSascha Wildner { INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH },
705db2f26eSSascha Wildner { INTR_TRIGGER_EDGE, INTR_POLARITY_LOW },
715db2f26eSSascha Wildner
725db2f26eSSascha Wildner /* Required last entry */
735db2f26eSSascha Wildner { INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM }
745db2f26eSSascha Wildner };
755db2f26eSSascha Wildner
76*6957c423SMatthew Dillon /*
77*6957c423SMatthew Dillon * Set to 1 to stop atkbdc from being configured early, via cninit().
78*6957c423SMatthew Dillon *
79*6957c423SMatthew Dillon * Currently set to 0 because this is causing problems for several
80*6957c423SMatthew Dillon * people. Can be set to 1 with a tunable.
81*6957c423SMatthew Dillon */
82*6957c423SMatthew Dillon int acpi_fadt_8042_nolegacy = 0;
83*6957c423SMatthew Dillon TUNABLE_INT("hw.acpi.fadt_8042_nolegacy", &acpi_fadt_8042_nolegacy);
84131acb03SImre Vadász
855db2f26eSSascha Wildner static void
fadt_probe(void)865db2f26eSSascha Wildner fadt_probe(void)
875db2f26eSSascha Wildner {
888d3ef488SSascha Wildner ACPI_TABLE_FADT *fadt;
895db2f26eSSascha Wildner vm_paddr_t fadt_paddr;
905db2f26eSSascha Wildner enum intr_trigger trig;
915db2f26eSSascha Wildner enum intr_polarity pola;
925db2f26eSSascha Wildner int enabled = 1;
935db2f26eSSascha Wildner char *env;
945db2f26eSSascha Wildner
958d3ef488SSascha Wildner fadt_paddr = sdt_search(ACPI_SIG_FADT);
965db2f26eSSascha Wildner if (fadt_paddr == 0) {
97131acb03SImre Vadász acpi_fadt_8042_nolegacy = 0;
985db2f26eSSascha Wildner kprintf("fadt_probe: can't locate FADT\n");
995db2f26eSSascha Wildner return;
1005db2f26eSSascha Wildner }
1015db2f26eSSascha Wildner
1025db2f26eSSascha Wildner fadt = sdt_sdth_map(fadt_paddr);
1035db2f26eSSascha Wildner KKASSERT(fadt != NULL);
1045db2f26eSSascha Wildner
1055db2f26eSSascha Wildner /*
10631fa8539SSascha Wildner * FADT in ACPI specification 1.0 - 6.0
1075db2f26eSSascha Wildner */
10831fa8539SSascha Wildner if (fadt->Header.Revision < 1 || fadt->Header.Revision > 6) {
109fa90647aSSepherosa Ziehau kprintf("fadt_probe: unknown FADT revision %d\n",
1108d3ef488SSascha Wildner fadt->Header.Revision);
1115db2f26eSSascha Wildner }
1125db2f26eSSascha Wildner
11343376c52SSascha Wildner if (fadt->Header.Length < ACPI_FADT_V1_SIZE) {
114131acb03SImre Vadász acpi_fadt_8042_nolegacy = 0;
11543376c52SSascha Wildner kprintf("fadt_probe: invalid FADT length %u (< %u)\n",
11643376c52SSascha Wildner fadt->Header.Length, ACPI_FADT_V1_SIZE);
1175db2f26eSSascha Wildner goto back;
1185db2f26eSSascha Wildner }
1195db2f26eSSascha Wildner
120131acb03SImre Vadász if (fadt->Header.Length >= ACPI_FADT_V3_SIZE &&
121131acb03SImre Vadász fadt->Header.Revision >= 3) {
122131acb03SImre Vadász if ((fadt->BootFlags & ACPI_FADT_8042) != 0)
123131acb03SImre Vadász acpi_fadt_8042_nolegacy = 0;
124131acb03SImre Vadász } else {
125131acb03SImre Vadász acpi_fadt_8042_nolegacy = 0;
126131acb03SImre Vadász }
127131acb03SImre Vadász
1285db2f26eSSascha Wildner kgetenv_int("hw.acpi.sci.enabled", &enabled);
1295db2f26eSSascha Wildner if (!enabled)
1305db2f26eSSascha Wildner goto back;
1315db2f26eSSascha Wildner
1328d3ef488SSascha Wildner acpi_sci_irq = fadt->SciInterrupt;
1335db2f26eSSascha Wildner
1345db2f26eSSascha Wildner env = kgetenv("hw.acpi.sci.trigger");
1355db2f26eSSascha Wildner if (env == NULL)
1365db2f26eSSascha Wildner goto back;
1375db2f26eSSascha Wildner
1385db2f26eSSascha Wildner trig = INTR_TRIGGER_CONFORM;
1395db2f26eSSascha Wildner if (strcmp(env, "edge") == 0)
1405db2f26eSSascha Wildner trig = INTR_TRIGGER_EDGE;
1415db2f26eSSascha Wildner else if (strcmp(env, "level") == 0)
1425db2f26eSSascha Wildner trig = INTR_TRIGGER_LEVEL;
1435db2f26eSSascha Wildner
1445db2f26eSSascha Wildner kfreeenv(env);
1455db2f26eSSascha Wildner
1465db2f26eSSascha Wildner if (trig == INTR_TRIGGER_CONFORM)
1475db2f26eSSascha Wildner goto back;
1485db2f26eSSascha Wildner
1495db2f26eSSascha Wildner env = kgetenv("hw.acpi.sci.polarity");
1505db2f26eSSascha Wildner if (env == NULL)
1515db2f26eSSascha Wildner goto back;
1525db2f26eSSascha Wildner
1535db2f26eSSascha Wildner pola = INTR_POLARITY_CONFORM;
1545db2f26eSSascha Wildner if (strcmp(env, "high") == 0)
1555db2f26eSSascha Wildner pola = INTR_POLARITY_HIGH;
1565db2f26eSSascha Wildner else if (strcmp(env, "low") == 0)
1575db2f26eSSascha Wildner pola = INTR_POLARITY_LOW;
1585db2f26eSSascha Wildner
1595db2f26eSSascha Wildner kfreeenv(env);
1605db2f26eSSascha Wildner
1615db2f26eSSascha Wildner if (pola == INTR_POLARITY_CONFORM)
1625db2f26eSSascha Wildner goto back;
1635db2f26eSSascha Wildner
1645db2f26eSSascha Wildner acpi_sci_trig = trig;
1655db2f26eSSascha Wildner acpi_sci_pola = pola;
1665db2f26eSSascha Wildner back:
1675db2f26eSSascha Wildner if (acpi_sci_irq >= 0) {
1685db2f26eSSascha Wildner FADT_VPRINTF("SCI irq %d, %s/%s\n", acpi_sci_irq,
1695db2f26eSSascha Wildner intr_str_trigger(acpi_sci_trig),
1705db2f26eSSascha Wildner intr_str_polarity(acpi_sci_pola));
1715db2f26eSSascha Wildner } else {
1725db2f26eSSascha Wildner FADT_VPRINTF("SCI is disabled\n");
1735db2f26eSSascha Wildner }
1748d3ef488SSascha Wildner sdt_sdth_unmap(&fadt->Header);
1755db2f26eSSascha Wildner }
1765db2f26eSSascha Wildner SYSINIT(fadt_probe, SI_BOOT2_PRESMP, SI_ORDER_SECOND, fadt_probe, 0);
1775db2f26eSSascha Wildner
1785db2f26eSSascha Wildner static void
acpi_sci_dummy_intr(void * dummy __unused,void * frame __unused)1795db2f26eSSascha Wildner acpi_sci_dummy_intr(void *dummy __unused, void *frame __unused)
1805db2f26eSSascha Wildner {
1815db2f26eSSascha Wildner }
1825db2f26eSSascha Wildner
1835db2f26eSSascha Wildner static boolean_t
acpi_sci_test(const struct acpi_sci_mode * mode)1845db2f26eSSascha Wildner acpi_sci_test(const struct acpi_sci_mode *mode)
1855db2f26eSSascha Wildner {
1865db2f26eSSascha Wildner void *sci_desc;
1875db2f26eSSascha Wildner long last_cnt;
1885db2f26eSSascha Wildner
1895db2f26eSSascha Wildner FADT_VPRINTF("SCI testing %s/%s\n",
1905db2f26eSSascha Wildner intr_str_trigger(mode->sci_trig),
1915db2f26eSSascha Wildner intr_str_polarity(mode->sci_pola));
1925db2f26eSSascha Wildner
1935db2f26eSSascha Wildner last_cnt = get_interrupt_counter(acpi_sci_irq, 0);
1945db2f26eSSascha Wildner
1955db2f26eSSascha Wildner machintr_legacy_intr_config(acpi_sci_irq,
1965db2f26eSSascha Wildner mode->sci_trig, mode->sci_pola);
1975db2f26eSSascha Wildner
1985db2f26eSSascha Wildner sci_desc = register_int(acpi_sci_irq,
1995db2f26eSSascha Wildner acpi_sci_dummy_intr, NULL, "sci", NULL,
2005db2f26eSSascha Wildner INTR_EXCL | INTR_CLOCK |
2015db2f26eSSascha Wildner INTR_NOPOLL | INTR_MPSAFE | INTR_NOENTROPY, 0);
2025db2f26eSSascha Wildner
2035db2f26eSSascha Wildner DELAY(100 * 1000);
2045db2f26eSSascha Wildner
2055db2f26eSSascha Wildner unregister_int(sci_desc, 0);
2065db2f26eSSascha Wildner
2075db2f26eSSascha Wildner if (get_interrupt_counter(acpi_sci_irq, 0) - last_cnt < 20) {
2085db2f26eSSascha Wildner acpi_sci_trig = mode->sci_trig;
2095db2f26eSSascha Wildner acpi_sci_pola = mode->sci_pola;
2105db2f26eSSascha Wildner
2115db2f26eSSascha Wildner kprintf("ACPI FADT: SCI select %s/%s\n",
2125db2f26eSSascha Wildner intr_str_trigger(acpi_sci_trig),
2135db2f26eSSascha Wildner intr_str_polarity(acpi_sci_pola));
2145db2f26eSSascha Wildner return TRUE;
2155db2f26eSSascha Wildner }
2165db2f26eSSascha Wildner return FALSE;
2175db2f26eSSascha Wildner }
2185db2f26eSSascha Wildner
2195db2f26eSSascha Wildner void
acpi_sci_config(void)2205db2f26eSSascha Wildner acpi_sci_config(void)
2215db2f26eSSascha Wildner {
2225db2f26eSSascha Wildner const struct acpi_sci_mode *mode;
2235db2f26eSSascha Wildner
2245db2f26eSSascha Wildner KKASSERT(mycpuid == 0);
2255db2f26eSSascha Wildner
2265db2f26eSSascha Wildner if (machintr_legacy_intr_find(acpi_sci_irq,
2275db2f26eSSascha Wildner INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM) < 0) {
2285db2f26eSSascha Wildner kprintf("ACPI FADT: SCI irq %d is invalid, disable\n",
2295db2f26eSSascha Wildner acpi_sci_irq);
2305db2f26eSSascha Wildner acpi_sci_irq = -1;
2315db2f26eSSascha Wildner return;
2325db2f26eSSascha Wildner }
2335db2f26eSSascha Wildner
2345db2f26eSSascha Wildner if (acpi_sci_irq < 0)
2355db2f26eSSascha Wildner return;
2365db2f26eSSascha Wildner
2375db2f26eSSascha Wildner if (acpi_sci_trig != INTR_TRIGGER_CONFORM) {
2385db2f26eSSascha Wildner KKASSERT(acpi_sci_pola != INTR_POLARITY_CONFORM);
2395db2f26eSSascha Wildner machintr_legacy_intr_config(acpi_sci_irq,
2405db2f26eSSascha Wildner acpi_sci_trig, acpi_sci_pola);
2415db2f26eSSascha Wildner return;
2425db2f26eSSascha Wildner }
2435db2f26eSSascha Wildner
2445db2f26eSSascha Wildner kprintf("ACPI FADT: SCI testing interrupt mode ...\n");
2455db2f26eSSascha Wildner if (acpi_sci_mode1 != NULL) {
2465db2f26eSSascha Wildner if (acpi_sci_test(acpi_sci_mode1))
2475db2f26eSSascha Wildner return;
2485db2f26eSSascha Wildner }
2495db2f26eSSascha Wildner for (mode = acpi_sci_modes; mode->sci_trig != INTR_TRIGGER_CONFORM;
2505db2f26eSSascha Wildner ++mode) {
2515db2f26eSSascha Wildner if (mode == acpi_sci_mode1)
2525db2f26eSSascha Wildner continue;
2535db2f26eSSascha Wildner if (acpi_sci_test(mode))
2545db2f26eSSascha Wildner return;
2555db2f26eSSascha Wildner }
2565db2f26eSSascha Wildner
2575db2f26eSSascha Wildner kprintf("ACPI FADT: no suitable interrupt mode for SCI, disable\n");
2585db2f26eSSascha Wildner acpi_sci_irq = -1;
2595db2f26eSSascha Wildner }
2605db2f26eSSascha Wildner
2615db2f26eSSascha Wildner int
acpi_sci_enabled(void)2625db2f26eSSascha Wildner acpi_sci_enabled(void)
2635db2f26eSSascha Wildner {
2645db2f26eSSascha Wildner if (acpi_sci_irq >= 0)
2655db2f26eSSascha Wildner return 1;
2665db2f26eSSascha Wildner else
2675db2f26eSSascha Wildner return 0;
2685db2f26eSSascha Wildner }
2695db2f26eSSascha Wildner
2705db2f26eSSascha Wildner int
acpi_sci_pci_shareable(void)271737d9e74SSascha Wildner acpi_sci_pci_shareable(void)
2725db2f26eSSascha Wildner {
2735db2f26eSSascha Wildner if (acpi_sci_irq >= 0 &&
2745db2f26eSSascha Wildner acpi_sci_trig == INTR_TRIGGER_LEVEL &&
2755db2f26eSSascha Wildner acpi_sci_pola == INTR_POLARITY_LOW)
2765db2f26eSSascha Wildner return 1;
2775db2f26eSSascha Wildner else
2785db2f26eSSascha Wildner return 0;
2795db2f26eSSascha Wildner }
2805db2f26eSSascha Wildner
2815db2f26eSSascha Wildner int
acpi_sci_irqno(void)2825db2f26eSSascha Wildner acpi_sci_irqno(void)
2835db2f26eSSascha Wildner {
2845db2f26eSSascha Wildner return acpi_sci_irq;
2855db2f26eSSascha Wildner }
2865db2f26eSSascha Wildner
2875db2f26eSSascha Wildner void
acpi_sci_setmode1(enum intr_trigger trig,enum intr_polarity pola)2885db2f26eSSascha Wildner acpi_sci_setmode1(enum intr_trigger trig, enum intr_polarity pola)
2895db2f26eSSascha Wildner {
2905db2f26eSSascha Wildner const struct acpi_sci_mode *mode;
2915db2f26eSSascha Wildner
2925db2f26eSSascha Wildner for (mode = acpi_sci_modes; mode->sci_trig != INTR_TRIGGER_CONFORM;
2935db2f26eSSascha Wildner ++mode) {
2945db2f26eSSascha Wildner if (mode->sci_trig == trig && mode->sci_pola == pola) {
2955db2f26eSSascha Wildner acpi_sci_mode1 = mode;
2965db2f26eSSascha Wildner return;
2975db2f26eSSascha Wildner }
2985db2f26eSSascha Wildner }
2995db2f26eSSascha Wildner }
300