1 /* $OpenBSD: acpiprt.c,v 1.43 2010/08/03 22:54:12 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/proc.h> 20 #include <sys/signalvar.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/bus.h> 26 27 #include <dev/acpi/acpireg.h> 28 #include <dev/acpi/acpivar.h> 29 #include <dev/acpi/acpidev.h> 30 #include <dev/acpi/amltypes.h> 31 #include <dev/acpi/dsdt.h> 32 33 #include <dev/pci/pcivar.h> 34 #include <dev/pci/pcidevs.h> 35 #include <dev/pci/ppbreg.h> 36 37 #include <machine/i82093reg.h> 38 #include <machine/i82093var.h> 39 40 #include <machine/mpbiosvar.h> 41 42 #include "ioapic.h" 43 44 struct acpiprt_map { 45 int bus, dev; 46 int pin; 47 int irq; 48 struct acpiprt_softc *sc; 49 struct aml_node *node; 50 SIMPLEQ_ENTRY(acpiprt_map) list; 51 }; 52 53 SIMPLEQ_HEAD(, acpiprt_map) acpiprt_map_list = 54 SIMPLEQ_HEAD_INITIALIZER(acpiprt_map_list); 55 56 int acpiprt_match(struct device *, void *, void *); 57 void acpiprt_attach(struct device *, struct device *, void *); 58 int acpiprt_getirq(union acpi_resource *crs, void *arg); 59 int acpiprt_chooseirq(union acpi_resource *, void *); 60 61 struct acpiprt_softc { 62 struct device sc_dev; 63 64 struct acpi_softc *sc_acpi; 65 struct aml_node *sc_devnode; 66 67 int sc_bus; 68 }; 69 70 struct cfattach acpiprt_ca = { 71 sizeof(struct acpiprt_softc), acpiprt_match, acpiprt_attach 72 }; 73 74 struct cfdriver acpiprt_cd = { 75 NULL, "acpiprt", DV_DULL 76 }; 77 78 void acpiprt_prt_add(struct acpiprt_softc *, struct aml_value *); 79 int acpiprt_getpcibus(struct acpiprt_softc *, struct aml_node *); 80 void acpiprt_route_interrupt(int bus, int dev, int pin); 81 82 int 83 acpiprt_match(struct device *parent, void *match, void *aux) 84 { 85 struct acpi_attach_args *aa = aux; 86 struct cfdata *cf = match; 87 88 /* sanity */ 89 if (aa->aaa_name == NULL || 90 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || 91 aa->aaa_table != NULL) 92 return (0); 93 94 return (1); 95 } 96 97 void 98 acpiprt_attach(struct device *parent, struct device *self, void *aux) 99 { 100 struct acpiprt_softc *sc = (struct acpiprt_softc *)self; 101 struct acpi_attach_args *aa = aux; 102 struct aml_value res; 103 int i; 104 105 sc->sc_acpi = (struct acpi_softc *)parent; 106 sc->sc_devnode = aa->aaa_node; 107 sc->sc_bus = acpiprt_getpcibus(sc, sc->sc_devnode); 108 printf(": bus %d (%s)", sc->sc_bus, sc->sc_devnode->parent->name); 109 110 if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res)) { 111 printf(": no PCI interrupt routing table\n"); 112 return; 113 } 114 115 if (res.type != AML_OBJTYPE_PACKAGE) { 116 printf(": _PRT is not a package\n"); 117 aml_freevalue(&res); 118 return; 119 } 120 121 printf("\n"); 122 123 if (sc->sc_bus == -1) 124 return; 125 126 for (i = 0; i < res.length; i++) 127 acpiprt_prt_add(sc, res.v_package[i]); 128 129 aml_freevalue(&res); 130 } 131 132 int 133 acpiprt_getirq(union acpi_resource *crs, void *arg) 134 { 135 int *irq = (int *)arg; 136 int typ; 137 138 typ = AML_CRSTYPE(crs); 139 switch (typ) { 140 case SR_IRQ: 141 *irq = ffs(letoh16(crs->sr_irq.irq_mask)) - 1; 142 break; 143 case LR_EXTIRQ: 144 *irq = letoh32(crs->lr_extirq.irq[0]); 145 break; 146 default: 147 printf("unknown interrupt: %x\n", typ); 148 } 149 return (0); 150 } 151 152 int 153 acpiprt_pri[16] = { 154 0, /* 8254 Counter 0 */ 155 1, /* Keyboard */ 156 0, /* 8259 Slave */ 157 2, /* Serial Port A */ 158 2, /* Serial Port B */ 159 5, /* Parallel Port / Generic */ 160 2, /* Floppy Disk */ 161 4, /* Parallel Port / Generic */ 162 1, /* RTC */ 163 6, /* Generic */ 164 7, /* Generic */ 165 7, /* Generic */ 166 1, /* Mouse */ 167 0, /* FPU */ 168 2, /* Primary IDE */ 169 3 /* Secondary IDE */ 170 }; 171 172 int 173 acpiprt_chooseirq(union acpi_resource *crs, void *arg) 174 { 175 int *irq = (int *)arg; 176 int typ, i, pri = -1; 177 178 typ = AML_CRSTYPE(crs); 179 switch (typ) { 180 case SR_IRQ: 181 for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) { 182 if (crs->sr_irq.irq_mask & (1 << i) && 183 acpiprt_pri[i] > pri) { 184 *irq = i; 185 pri = acpiprt_pri[*irq]; 186 } 187 } 188 break; 189 case LR_EXTIRQ: 190 /* First try non-8259 interrupts. */ 191 for (i = 0; i < crs->lr_extirq.irq_count; i++) { 192 if (crs->lr_extirq.irq[i] > 15) { 193 *irq = crs->lr_extirq.irq[i]; 194 return (0); 195 } 196 } 197 198 for (i = 0; i < crs->lr_extirq.irq_count; i++) { 199 if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) { 200 *irq = crs->lr_extirq.irq[i]; 201 pri = acpiprt_pri[*irq]; 202 } 203 } 204 break; 205 default: 206 printf("unknown interrupt: %x\n", typ); 207 } 208 return (0); 209 } 210 211 void 212 acpiprt_prt_add(struct acpiprt_softc *sc, struct aml_value *v) 213 { 214 struct aml_node *node; 215 struct aml_value res, *pp; 216 u_int64_t addr; 217 int pin, irq; 218 int64_t sta; 219 #if NIOAPIC > 0 220 struct mp_intr_map *map; 221 struct ioapic_softc *apic; 222 #endif 223 pci_chipset_tag_t pc = NULL; 224 pcitag_t tag; 225 pcireg_t reg; 226 int bus, dev, func, nfuncs; 227 struct acpiprt_map *p; 228 229 if (v->type != AML_OBJTYPE_PACKAGE || v->length != 4) { 230 printf("invalid mapping object\n"); 231 return; 232 } 233 234 addr = aml_val2int(v->v_package[0]); 235 pin = aml_val2int(v->v_package[1]); 236 if (pin > 3) { 237 return; 238 } 239 240 pp = v->v_package[2]; 241 if (pp->type == AML_OBJTYPE_STRING) { 242 node = aml_searchrel(sc->sc_devnode, pp->v_string); 243 if (node == NULL) { 244 printf("Invalid device\n"); 245 return; 246 } 247 pp = node->value; 248 } 249 if (pp->type == AML_OBJTYPE_NAMEREF) { 250 node = aml_searchrel(sc->sc_devnode, pp->v_nameref); 251 if (node == NULL) { 252 printf("Invalid device\n"); 253 return; 254 } 255 pp = node->value; 256 } 257 if (pp->type == AML_OBJTYPE_OBJREF) { 258 pp = pp->v_objref.ref; 259 } 260 if (pp->type == AML_OBJTYPE_DEVICE) { 261 node = pp->node; 262 if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) { 263 printf("no _STA method\n"); 264 return; 265 } 266 267 if ((sta & STA_PRESENT) == 0) 268 return; 269 270 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { 271 printf("no _CRS method\n"); 272 return; 273 } 274 275 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 276 printf("invalid _CRS object\n"); 277 aml_freevalue(&res); 278 return; 279 } 280 aml_parse_resource(&res, acpiprt_getirq, &irq); 281 aml_freevalue(&res); 282 283 /* Pick a new IRQ if necessary. */ 284 if ((irq == 0 || irq == 2 || irq == 13) && 285 !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){ 286 aml_parse_resource(&res, acpiprt_chooseirq, &irq); 287 aml_freevalue(&res); 288 } 289 290 if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL) 291 return; 292 p->bus = sc->sc_bus; 293 p->dev = ACPI_PCI_DEV(addr << 16); 294 p->pin = pin; 295 p->irq = irq; 296 p->sc = sc; 297 p->node = node; 298 SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list); 299 } else { 300 irq = aml_val2int(v->v_package[3]); 301 } 302 303 #ifdef ACPI_DEBUG 304 printf("%s: %s addr 0x%llx pin %d irq %d\n", 305 DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq); 306 #endif 307 308 #if NIOAPIC > 0 309 if (nioapics > 0) { 310 apic = ioapic_find_bybase(irq); 311 if (apic == NULL) { 312 printf("%s: no apic found for irq %d\n", DEVNAME(sc), irq); 313 return; 314 } 315 316 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO); 317 if (map == NULL) 318 return; 319 320 map->ioapic = apic; 321 map->ioapic_pin = irq - apic->sc_apic_vecbase; 322 map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3); 323 map->redir = IOAPIC_REDLO_ACTLO | IOAPIC_REDLO_LEVEL; 324 map->redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); 325 326 map->ioapic_ih = APIC_INT_VIA_APIC | 327 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | 328 (map->ioapic_pin << APIC_INT_PIN_SHIFT)); 329 330 apic->sc_pins[map->ioapic_pin].ip_map = map; 331 332 map->next = mp_busses[sc->sc_bus].mb_intrs; 333 mp_busses[sc->sc_bus].mb_intrs = map; 334 335 return; 336 } 337 #endif 338 339 bus = sc->sc_bus; 340 dev = ACPI_PCI_DEV(addr << 16); 341 tag = pci_make_tag(pc, bus, dev, 0); 342 343 reg = pci_conf_read(pc, tag, PCI_BHLC_REG); 344 if (PCI_HDRTYPE_MULTIFN(reg)) 345 nfuncs = 8; 346 else 347 nfuncs = 1; 348 349 for (func = 0; func < nfuncs; func++) { 350 tag = pci_make_tag(pc, bus, dev, func); 351 reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); 352 if (PCI_INTERRUPT_PIN(reg) == pin + 1) { 353 reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT); 354 reg |= irq << PCI_INTERRUPT_LINE_SHIFT; 355 pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg); 356 } 357 } 358 } 359 360 int 361 acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node) 362 { 363 /* Check if parent device has PCI mapping */ 364 return (node->parent && node->parent->pci) ? 365 node->parent->pci->sub : -1; 366 } 367 368 void 369 acpiprt_route_interrupt(int bus, int dev, int pin) 370 { 371 struct acpiprt_softc *sc; 372 struct acpiprt_map *p; 373 struct aml_node *node = NULL; 374 struct aml_value res, res2; 375 union acpi_resource *crs; 376 int irq, newirq; 377 int64_t sta; 378 379 SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) { 380 if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) { 381 newirq = p->irq; 382 sc = p->sc; 383 node = p->node; 384 break; 385 } 386 } 387 if (node == NULL) 388 return; 389 390 if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) { 391 printf("no _STA method\n"); 392 return; 393 } 394 395 KASSERT(sta & STA_PRESENT); 396 397 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { 398 printf("no _CRS method\n"); 399 return; 400 } 401 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 402 printf("invalid _CRS object\n"); 403 aml_freevalue(&res); 404 return; 405 } 406 aml_parse_resource(&res, acpiprt_getirq, &irq); 407 408 /* Only re-route interrupts when necessary. */ 409 if ((sta & STA_ENABLED) && irq == newirq) { 410 aml_freevalue(&res); 411 return; 412 } 413 414 crs = (union acpi_resource *)res.v_buffer; 415 switch (AML_CRSTYPE(crs)) { 416 case SR_IRQ: 417 crs->sr_irq.irq_mask = htole16(1 << newirq); 418 break; 419 case LR_EXTIRQ: 420 crs->lr_extirq.irq[0] = htole32(newirq); 421 break; 422 } 423 424 if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) { 425 printf("no _SRS method\n"); 426 aml_freevalue(&res); 427 return; 428 } 429 aml_freevalue(&res); 430 aml_freevalue(&res2); 431 } 432