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/interrupt.h> 38 #include <sys/kernel.h> 39 #include <sys/machintr.h> 40 #include <sys/systm.h> 41 #include <sys/thread2.h> 42 43 #include "acpi_sdt.h" 44 #include "acpi_sdt_var.h" 45 #include "acpi_sci_var.h" 46 47 #define FADT_VPRINTF(fmt, arg...) \ 48 do { \ 49 if (bootverbose) \ 50 kprintf("ACPI FADT: " fmt , ##arg); \ 51 } while (0) 52 53 /* Fixed ACPI Description Table */ 54 struct acpi_fadt { 55 struct acpi_sdth fadt_hdr; 56 uint32_t fadt_fw_ctrl; 57 uint32_t fadt_dsdt; 58 uint8_t fadt_rsvd1; 59 uint8_t fadt_pm_prof; 60 uint16_t fadt_sci_int; 61 uint32_t fadt_smi_cmd; 62 uint8_t fadt_acpi_en; 63 uint8_t fadt_acpi_dis; 64 uint8_t fadt_s4bios; 65 uint8_t fadt_pstate; 66 /* More ... */ 67 } __packed; 68 69 struct acpi_sci_mode { 70 enum intr_trigger sci_trig; 71 enum intr_polarity sci_pola; 72 }; 73 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 static const struct acpi_sci_mode *acpi_sci_mode1 = NULL; 78 79 static const struct acpi_sci_mode acpi_sci_modes[] = { 80 /* 81 * NOTE: Order is critical 82 */ 83 { INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW }, 84 { INTR_TRIGGER_LEVEL, INTR_POLARITY_HIGH }, 85 { INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH }, 86 { INTR_TRIGGER_EDGE, INTR_POLARITY_LOW }, 87 88 /* Required last entry */ 89 { INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM } 90 }; 91 92 static void 93 fadt_probe(void) 94 { 95 struct acpi_fadt *fadt; 96 vm_paddr_t fadt_paddr; 97 enum intr_trigger trig; 98 enum intr_polarity pola; 99 int enabled = 1; 100 char *env; 101 102 fadt_paddr = sdt_search(ACPI_FADT_SIG); 103 if (fadt_paddr == 0) { 104 kprintf("fadt_probe: can't locate FADT\n"); 105 return; 106 } 107 108 fadt = sdt_sdth_map(fadt_paddr); 109 KKASSERT(fadt != NULL); 110 111 /* 112 * FADT in ACPI specification 1.0 - 5.0 113 */ 114 if (fadt->fadt_hdr.sdth_rev < 1 || fadt->fadt_hdr.sdth_rev > 5) { 115 kprintf("fadt_probe: unknown FADT revision %d\n", 116 fadt->fadt_hdr.sdth_rev); 117 } 118 119 if (fadt->fadt_hdr.sdth_len < sizeof(*fadt)) { 120 kprintf("fadt_probe: invalid FADT length %u\n", 121 fadt->fadt_hdr.sdth_len); 122 goto back; 123 } 124 125 kgetenv_int("hw.acpi.sci.enabled", &enabled); 126 if (!enabled) 127 goto back; 128 129 acpi_sci_irq = fadt->fadt_sci_int; 130 131 env = kgetenv("hw.acpi.sci.trigger"); 132 if (env == NULL) 133 goto back; 134 135 trig = INTR_TRIGGER_CONFORM; 136 if (strcmp(env, "edge") == 0) 137 trig = INTR_TRIGGER_EDGE; 138 else if (strcmp(env, "level") == 0) 139 trig = INTR_TRIGGER_LEVEL; 140 141 kfreeenv(env); 142 143 if (trig == INTR_TRIGGER_CONFORM) 144 goto back; 145 146 env = kgetenv("hw.acpi.sci.polarity"); 147 if (env == NULL) 148 goto back; 149 150 pola = INTR_POLARITY_CONFORM; 151 if (strcmp(env, "high") == 0) 152 pola = INTR_POLARITY_HIGH; 153 else if (strcmp(env, "low") == 0) 154 pola = INTR_POLARITY_LOW; 155 156 kfreeenv(env); 157 158 if (pola == INTR_POLARITY_CONFORM) 159 goto back; 160 161 acpi_sci_trig = trig; 162 acpi_sci_pola = pola; 163 back: 164 if (acpi_sci_irq >= 0) { 165 FADT_VPRINTF("SCI irq %d, %s/%s\n", acpi_sci_irq, 166 intr_str_trigger(acpi_sci_trig), 167 intr_str_polarity(acpi_sci_pola)); 168 } else { 169 FADT_VPRINTF("SCI is disabled\n"); 170 } 171 sdt_sdth_unmap(&fadt->fadt_hdr); 172 } 173 SYSINIT(fadt_probe, SI_BOOT2_PRESMP, SI_ORDER_SECOND, fadt_probe, 0); 174 175 static void 176 acpi_sci_dummy_intr(void *dummy __unused, void *frame __unused) 177 { 178 } 179 180 static boolean_t 181 acpi_sci_test(const struct acpi_sci_mode *mode) 182 { 183 void *sci_desc; 184 long last_cnt; 185 186 FADT_VPRINTF("SCI testing %s/%s\n", 187 intr_str_trigger(mode->sci_trig), 188 intr_str_polarity(mode->sci_pola)); 189 190 last_cnt = get_interrupt_counter(acpi_sci_irq, 0); 191 192 machintr_legacy_intr_config(acpi_sci_irq, 193 mode->sci_trig, mode->sci_pola); 194 195 sci_desc = register_int(acpi_sci_irq, 196 acpi_sci_dummy_intr, NULL, "sci", NULL, 197 INTR_EXCL | INTR_CLOCK | 198 INTR_NOPOLL | INTR_MPSAFE | INTR_NOENTROPY, 0); 199 200 DELAY(100 * 1000); 201 202 unregister_int(sci_desc, 0); 203 204 if (get_interrupt_counter(acpi_sci_irq, 0) - last_cnt < 20) { 205 acpi_sci_trig = mode->sci_trig; 206 acpi_sci_pola = mode->sci_pola; 207 208 kprintf("ACPI FADT: SCI select %s/%s\n", 209 intr_str_trigger(acpi_sci_trig), 210 intr_str_polarity(acpi_sci_pola)); 211 return TRUE; 212 } 213 return FALSE; 214 } 215 216 void 217 acpi_sci_config(void) 218 { 219 const struct acpi_sci_mode *mode; 220 221 KKASSERT(mycpuid == 0); 222 223 if (machintr_legacy_intr_find(acpi_sci_irq, 224 INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM) < 0) { 225 kprintf("ACPI FADT: SCI irq %d is invalid, disable\n", 226 acpi_sci_irq); 227 acpi_sci_irq = -1; 228 return; 229 } 230 231 if (acpi_sci_irq < 0) 232 return; 233 234 if (acpi_sci_trig != INTR_TRIGGER_CONFORM) { 235 KKASSERT(acpi_sci_pola != INTR_POLARITY_CONFORM); 236 machintr_legacy_intr_config(acpi_sci_irq, 237 acpi_sci_trig, acpi_sci_pola); 238 return; 239 } 240 241 kprintf("ACPI FADT: SCI testing interrupt mode ...\n"); 242 if (acpi_sci_mode1 != NULL) { 243 if (acpi_sci_test(acpi_sci_mode1)) 244 return; 245 } 246 for (mode = acpi_sci_modes; mode->sci_trig != INTR_TRIGGER_CONFORM; 247 ++mode) { 248 if (mode == acpi_sci_mode1) 249 continue; 250 if (acpi_sci_test(mode)) 251 return; 252 } 253 254 kprintf("ACPI FADT: no suitable interrupt mode for SCI, disable\n"); 255 acpi_sci_irq = -1; 256 } 257 258 int 259 acpi_sci_enabled(void) 260 { 261 if (acpi_sci_irq >= 0) 262 return 1; 263 else 264 return 0; 265 } 266 267 int 268 acpi_sci_pci_shareable(void) 269 { 270 if (acpi_sci_irq >= 0 && 271 acpi_sci_trig == INTR_TRIGGER_LEVEL && 272 acpi_sci_pola == INTR_POLARITY_LOW) 273 return 1; 274 else 275 return 0; 276 } 277 278 int 279 acpi_sci_irqno(void) 280 { 281 return acpi_sci_irq; 282 } 283 284 void 285 acpi_sci_setmode1(enum intr_trigger trig, enum intr_polarity pola) 286 { 287 const struct acpi_sci_mode *mode; 288 289 for (mode = acpi_sci_modes; mode->sci_trig != INTR_TRIGGER_CONFORM; 290 ++mode) { 291 if (mode->sci_trig == trig && mode->sci_pola == pola) { 292 acpi_sci_mode1 = mode; 293 return; 294 } 295 } 296 } 297