acpi: Add functions to configure SCI and return SCI information
[dragonfly.git] / sys / platform / pc64 / acpica5 / acpi_fadt.c
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/kernel.h>
38 #include <sys/machintr.h>
39 #include <sys/systm.h>
40
41 #include <machine/pmap.h>
42 #include <machine/smp.h>
43 #include <machine/md_var.h>
44 #include <machine/specialreg.h>
45 #include <machine_base/apic/mpapic.h>
46
47 #include "acpi_sdt.h"
48 #include "acpi_sdt_var.h"
49 #include "acpi_sci_var.h"
50
51 #define FADT_VPRINTF(fmt, arg...) \
52 do { \
53         if (bootverbose) \
54                 kprintf("ACPI FADT: " fmt , ##arg); \
55 } while (0)
56
57 /* Fixed ACPI Description Table */
58 struct acpi_fadt {
59         struct acpi_sdth        fadt_hdr;
60         uint32_t                fadt_fw_ctrl;
61         uint32_t                fadt_dsdt;
62         uint8_t                 fadt_rsvd1;
63         uint8_t                 fadt_pm_prof;
64         uint16_t                fadt_sci_int;
65         uint32_t                fadt_smi_cmd;
66         uint8_t                 fadt_acpi_en;
67         uint8_t                 fadt_acpi_dis;
68         uint8_t                 fadt_s4bios;
69         uint8_t                 fadt_pstate;
70         /* More ... */
71 } __packed;
72
73 static int                      acpi_sci_mode_detect = 0;
74 static int                      acpi_sci_irq = -1;
75 static enum intr_trigger        acpi_sci_trig = INTR_TRIGGER_CONFORM;
76 static enum intr_polarity       acpi_sci_pola = INTR_POLARITY_CONFORM;
77
78 static void
79 fadt_probe(void)
80 {
81         struct acpi_fadt *fadt;
82         vm_paddr_t fadt_paddr;
83         enum intr_trigger trig;
84         enum intr_polarity pola;
85         int enabled = 1;
86         char *env;
87
88         fadt_paddr = sdt_search(ACPI_FADT_SIG);
89         if (fadt_paddr == 0) {
90                 kprintf("fadt_probe: can't locate FADT\n");
91                 return;
92         }
93
94         fadt = sdt_sdth_map(fadt_paddr);
95         KKASSERT(fadt != NULL);
96
97         /*
98          * FADT in ACPI specification 1.0 - 4.0
99          */
100         if (fadt->fadt_hdr.sdth_rev < 1 || fadt->fadt_hdr.sdth_rev > 4) {
101                 kprintf("fadt_probe: unsupported FADT revision %d\n",
102                         fadt->fadt_hdr.sdth_rev);
103                 goto back;
104         }
105
106         if (fadt->fadt_hdr.sdth_len < sizeof(*fadt)) {
107                 kprintf("fadt_probe: invalid FADT length %u\n",
108                         fadt->fadt_hdr.sdth_len);
109                 goto back;
110         }
111
112         kgetenv_int("hw.acpi.sci.enabled", &enabled);
113         if (!enabled)
114                 goto back;
115
116         acpi_sci_irq = fadt->fadt_sci_int;
117
118         env = kgetenv("hw.acpi.sci.trigger");
119         if (env == NULL)
120                 goto back;
121
122         trig = INTR_TRIGGER_CONFORM;
123         if (strcmp(env, "edge") == 0)
124                 trig = INTR_TRIGGER_EDGE;
125         else if (strcmp(env, "level") == 0)
126                 trig = INTR_TRIGGER_LEVEL;
127
128         kfreeenv(env);
129
130         if (trig == INTR_TRIGGER_CONFORM)
131                 goto back;
132
133         env = kgetenv("hw.acpi.sci.polarity");
134         if (env == NULL)
135                 goto back;
136
137         pola = INTR_POLARITY_CONFORM;
138         if (strcmp(env, "high") == 0)
139                 pola = INTR_POLARITY_HIGH;
140         else if (strcmp(env, "low") == 0)
141                 pola = INTR_POLARITY_LOW;
142
143         kfreeenv(env);
144
145         if (pola == INTR_POLARITY_CONFORM)
146                 goto back;
147
148         acpi_sci_trig = trig;
149         acpi_sci_pola = pola;
150 back:
151         if (acpi_sci_irq >= 0) {
152                 FADT_VPRINTF("SCI irq %d, %s/%s\n", acpi_sci_irq,
153                              intr_str_trigger(acpi_sci_trig),
154                              intr_str_polarity(acpi_sci_pola));
155         } else {
156                 FADT_VPRINTF("SCI is disabled\n");
157         }
158         sdt_sdth_unmap(&fadt->fadt_hdr);
159 }
160 SYSINIT(fadt_probe, SI_BOOT2_PRESMP, SI_ORDER_SECOND, fadt_probe, 0);
161
162 void
163 acpi_sci_config(void)
164 {
165         KKASSERT(acpi_sci_irq >= 0);
166
167         if (acpi_sci_trig != INTR_TRIGGER_CONFORM) {
168                 KKASSERT(acpi_sci_pola != INTR_POLARITY_CONFORM);
169                 machintr_intr_config(acpi_sci_irq,
170                     acpi_sci_trig, acpi_sci_pola);
171                 return;
172         }
173
174         if (!acpi_sci_mode_detect) {
175                 /*
176                  * XXX
177                  * DO NOT configure SCI according to ACPI specification
178                  * as level/low.  There are so many broken BIOSes that
179                  * could generate SCI as level/high, which will cause
180                  * interrupt storm on IRQ 9 if we by default configure
181                  * SCI as level/low; or BIOSes will generate SCI using
182                  * edge trigger, which may cause spurious interrupt
183                  * storm on IRQ 7 if 8259 used.  So to be safe, we just
184                  * configure SCI as edge/high at the cost that power
185                  * button may not work at all.
186                  */
187                 acpi_sci_trig = INTR_TRIGGER_EDGE;
188                 acpi_sci_pola = INTR_POLARITY_HIGH;
189
190                 machintr_intr_config(acpi_sci_irq,
191                     acpi_sci_trig, acpi_sci_pola);
192                 return;
193         }
194         /* TODO SCI mode detect */
195 }
196
197 int
198 acpi_sci_enabled(void)
199 {
200         if (acpi_sci_irq >= 0)
201                 return 1;
202         else
203                 return 0;
204 }
205
206 int
207 acpi_sci_pci_shariable(void)
208 {
209         if (acpi_sci_irq >= 0 &&
210             acpi_sci_trig == INTR_TRIGGER_LEVEL &&
211             acpi_sci_pola == INTR_POLARITY_LOW)
212                 return 1;
213         else
214                 return 0;
215 }