1 /* $OpenBSD: acpiprt.c,v 1.45 2014/05/02 14:10:15 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_irq { 45 int _int; 46 int _shr; 47 int _ll; 48 int _he; 49 }; 50 51 struct acpiprt_map { 52 int bus, dev; 53 int pin; 54 int irq; 55 struct acpiprt_softc *sc; 56 struct aml_node *node; 57 SIMPLEQ_ENTRY(acpiprt_map) list; 58 }; 59 60 SIMPLEQ_HEAD(, acpiprt_map) acpiprt_map_list = 61 SIMPLEQ_HEAD_INITIALIZER(acpiprt_map_list); 62 63 int acpiprt_match(struct device *, void *, void *); 64 void acpiprt_attach(struct device *, struct device *, void *); 65 int acpiprt_getirq(union acpi_resource *crs, void *arg); 66 int acpiprt_chooseirq(union acpi_resource *, void *); 67 68 struct acpiprt_softc { 69 struct device sc_dev; 70 71 struct acpi_softc *sc_acpi; 72 struct aml_node *sc_devnode; 73 74 int sc_bus; 75 }; 76 77 struct cfattach acpiprt_ca = { 78 sizeof(struct acpiprt_softc), acpiprt_match, acpiprt_attach 79 }; 80 81 struct cfdriver acpiprt_cd = { 82 NULL, "acpiprt", DV_DULL 83 }; 84 85 void acpiprt_prt_add(struct acpiprt_softc *, struct aml_value *); 86 int acpiprt_getpcibus(struct acpiprt_softc *, struct aml_node *); 87 void acpiprt_route_interrupt(int bus, int dev, int pin); 88 89 int 90 acpiprt_match(struct device *parent, void *match, void *aux) 91 { 92 struct acpi_attach_args *aa = aux; 93 struct cfdata *cf = match; 94 95 /* sanity */ 96 if (aa->aaa_name == NULL || 97 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || 98 aa->aaa_table != NULL) 99 return (0); 100 101 return (1); 102 } 103 104 void 105 acpiprt_attach(struct device *parent, struct device *self, void *aux) 106 { 107 struct acpiprt_softc *sc = (struct acpiprt_softc *)self; 108 struct acpi_attach_args *aa = aux; 109 struct aml_value res; 110 int i; 111 112 sc->sc_acpi = (struct acpi_softc *)parent; 113 sc->sc_devnode = aa->aaa_node; 114 sc->sc_bus = acpiprt_getpcibus(sc, sc->sc_devnode); 115 printf(": bus %d (%s)", sc->sc_bus, sc->sc_devnode->parent->name); 116 117 if (sc->sc_bus == -1) { 118 printf("\n"); 119 return; 120 } 121 122 if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res)) { 123 printf(": no PCI interrupt routing table\n"); 124 return; 125 } 126 127 if (res.type != AML_OBJTYPE_PACKAGE) { 128 printf(": _PRT is not a package\n"); 129 aml_freevalue(&res); 130 return; 131 } 132 133 printf("\n"); 134 135 for (i = 0; i < res.length; i++) 136 acpiprt_prt_add(sc, res.v_package[i]); 137 138 aml_freevalue(&res); 139 } 140 141 int 142 acpiprt_getirq(union acpi_resource *crs, void *arg) 143 { 144 struct acpiprt_irq *irq = arg; 145 int typ, len; 146 147 irq->_shr = 0; 148 irq->_ll = 0; 149 irq->_he = 1; 150 151 typ = AML_CRSTYPE(crs); 152 len = AML_CRSLEN(crs); 153 switch (typ) { 154 case SR_IRQ: 155 irq->_int= ffs(letoh16(crs->sr_irq.irq_mask)) - 1; 156 if (len > 2) { 157 irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR); 158 irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY); 159 irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE); 160 } 161 break; 162 case LR_EXTIRQ: 163 irq->_int = letoh32(crs->lr_extirq.irq[0]); 164 irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR); 165 irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY); 166 irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE); 167 break; 168 default: 169 printf("unknown interrupt: %x\n", typ); 170 } 171 return (0); 172 } 173 174 int 175 acpiprt_pri[16] = { 176 0, /* 8254 Counter 0 */ 177 1, /* Keyboard */ 178 0, /* 8259 Slave */ 179 2, /* Serial Port A */ 180 2, /* Serial Port B */ 181 5, /* Parallel Port / Generic */ 182 2, /* Floppy Disk */ 183 4, /* Parallel Port / Generic */ 184 1, /* RTC */ 185 6, /* Generic */ 186 7, /* Generic */ 187 7, /* Generic */ 188 1, /* Mouse */ 189 0, /* FPU */ 190 2, /* Primary IDE */ 191 3 /* Secondary IDE */ 192 }; 193 194 int 195 acpiprt_chooseirq(union acpi_resource *crs, void *arg) 196 { 197 struct acpiprt_irq *irq = arg; 198 int typ, len, i, pri = -1; 199 200 irq->_shr = 0; 201 irq->_ll = 0; 202 irq->_he = 1; 203 204 typ = AML_CRSTYPE(crs); 205 len = AML_CRSLEN(crs); 206 switch (typ) { 207 case SR_IRQ: 208 for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) { 209 if (crs->sr_irq.irq_mask & (1 << i) && 210 acpiprt_pri[i] > pri) { 211 irq->_int = i; 212 pri = acpiprt_pri[irq->_int]; 213 } 214 } 215 if (len > 2) { 216 irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR); 217 irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY); 218 irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE); 219 } 220 break; 221 case LR_EXTIRQ: 222 /* First try non-8259 interrupts. */ 223 for (i = 0; i < crs->lr_extirq.irq_count; i++) { 224 if (crs->lr_extirq.irq[i] > 15) { 225 irq->_int = crs->lr_extirq.irq[i]; 226 return (0); 227 } 228 } 229 230 for (i = 0; i < crs->lr_extirq.irq_count; i++) { 231 if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) { 232 irq->_int = crs->lr_extirq.irq[i]; 233 pri = acpiprt_pri[irq->_int]; 234 } 235 } 236 irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR); 237 irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY); 238 irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE); 239 break; 240 default: 241 printf("unknown interrupt: %x\n", typ); 242 } 243 return (0); 244 } 245 246 void 247 acpiprt_prt_add(struct acpiprt_softc *sc, struct aml_value *v) 248 { 249 struct aml_node *node; 250 struct aml_value res, *pp; 251 struct acpiprt_irq irq; 252 u_int64_t addr; 253 int pin; 254 int64_t sta; 255 #if NIOAPIC > 0 256 struct mp_intr_map *map; 257 struct ioapic_softc *apic; 258 #endif 259 pci_chipset_tag_t pc = NULL; 260 pcitag_t tag; 261 pcireg_t reg; 262 int bus, dev, func, nfuncs; 263 struct acpiprt_map *p; 264 265 if (v->type != AML_OBJTYPE_PACKAGE || v->length != 4) { 266 printf("invalid mapping object\n"); 267 return; 268 } 269 270 addr = aml_val2int(v->v_package[0]); 271 pin = aml_val2int(v->v_package[1]); 272 if (pin > 3) { 273 return; 274 } 275 276 pp = v->v_package[2]; 277 if (pp->type == AML_OBJTYPE_STRING) { 278 node = aml_searchrel(sc->sc_devnode, pp->v_string); 279 if (node == NULL) { 280 printf("Invalid device\n"); 281 return; 282 } 283 pp = node->value; 284 } 285 if (pp->type == AML_OBJTYPE_NAMEREF) { 286 node = aml_searchrel(sc->sc_devnode, pp->v_nameref); 287 if (node == NULL) { 288 printf("Invalid device\n"); 289 return; 290 } 291 pp = node->value; 292 } 293 if (pp->type == AML_OBJTYPE_OBJREF) { 294 pp = pp->v_objref.ref; 295 } 296 if (pp->type == AML_OBJTYPE_DEVICE) { 297 node = pp->node; 298 if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) { 299 printf("no _STA method\n"); 300 return; 301 } 302 303 if ((sta & STA_PRESENT) == 0) 304 return; 305 306 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { 307 printf("no _CRS method\n"); 308 return; 309 } 310 311 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 312 printf("invalid _CRS object\n"); 313 aml_freevalue(&res); 314 return; 315 } 316 aml_parse_resource(&res, acpiprt_getirq, &irq); 317 aml_freevalue(&res); 318 319 /* Pick a new IRQ if necessary. */ 320 if ((irq._int == 0 || irq._int == 2 || irq._int == 13) && 321 !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){ 322 aml_parse_resource(&res, acpiprt_chooseirq, &irq); 323 aml_freevalue(&res); 324 } 325 326 if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL) 327 return; 328 p->bus = sc->sc_bus; 329 p->dev = ACPI_PCI_DEV(addr << 16); 330 p->pin = pin; 331 p->irq = irq._int; 332 p->sc = sc; 333 p->node = node; 334 SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list); 335 } else { 336 irq._int = aml_val2int(v->v_package[3]); 337 irq._shr = 1; 338 irq._ll = 1; 339 irq._he = 0; 340 } 341 342 #ifdef ACPI_DEBUG 343 printf("%s: %s addr 0x%llx pin %d irq %d\n", 344 DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int); 345 #endif 346 347 #if NIOAPIC > 0 348 if (nioapics > 0) { 349 apic = ioapic_find_bybase(irq._int); 350 if (apic == NULL) { 351 printf("%s: no apic found for irq %d\n", 352 DEVNAME(sc), irq._int); 353 return; 354 } 355 356 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO); 357 if (map == NULL) 358 return; 359 360 map->ioapic = apic; 361 map->ioapic_pin = irq._int - apic->sc_apic_vecbase; 362 map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3); 363 if (irq._ll) 364 map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT); 365 else 366 map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT); 367 if (irq._he) 368 map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT); 369 else 370 map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT); 371 372 map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); 373 switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) { 374 case MPS_INTPO_DEF: 375 case MPS_INTPO_ACTLO: 376 map->redir |= IOAPIC_REDLO_ACTLO; 377 break; 378 } 379 switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) { 380 case MPS_INTTR_DEF: 381 case MPS_INTTR_LEVEL: 382 map->redir |= IOAPIC_REDLO_LEVEL; 383 break; 384 } 385 386 map->ioapic_ih = APIC_INT_VIA_APIC | 387 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | 388 (map->ioapic_pin << APIC_INT_PIN_SHIFT)); 389 390 apic->sc_pins[map->ioapic_pin].ip_map = map; 391 392 map->next = mp_busses[sc->sc_bus].mb_intrs; 393 mp_busses[sc->sc_bus].mb_intrs = map; 394 395 return; 396 } 397 #endif 398 399 bus = sc->sc_bus; 400 dev = ACPI_PCI_DEV(addr << 16); 401 tag = pci_make_tag(pc, bus, dev, 0); 402 403 reg = pci_conf_read(pc, tag, PCI_BHLC_REG); 404 if (PCI_HDRTYPE_MULTIFN(reg)) 405 nfuncs = 8; 406 else 407 nfuncs = 1; 408 409 for (func = 0; func < nfuncs; func++) { 410 tag = pci_make_tag(pc, bus, dev, func); 411 reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); 412 if (PCI_INTERRUPT_PIN(reg) == pin + 1) { 413 reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT); 414 reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT; 415 pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg); 416 } 417 } 418 } 419 420 int 421 acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node) 422 { 423 /* Check if parent device has PCI mapping */ 424 return (node->parent && node->parent->pci) ? 425 node->parent->pci->sub : -1; 426 } 427 428 void 429 acpiprt_route_interrupt(int bus, int dev, int pin) 430 { 431 struct acpiprt_softc *sc; 432 struct acpiprt_map *p; 433 struct acpiprt_irq irq; 434 struct aml_node *node = NULL; 435 struct aml_value res, res2; 436 union acpi_resource *crs; 437 int newirq; 438 int64_t sta; 439 440 SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) { 441 if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) { 442 newirq = p->irq; 443 sc = p->sc; 444 node = p->node; 445 break; 446 } 447 } 448 if (node == NULL) 449 return; 450 451 if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) { 452 printf("no _STA method\n"); 453 return; 454 } 455 456 KASSERT(sta & STA_PRESENT); 457 458 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { 459 printf("no _CRS method\n"); 460 return; 461 } 462 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 463 printf("invalid _CRS object\n"); 464 aml_freevalue(&res); 465 return; 466 } 467 aml_parse_resource(&res, acpiprt_getirq, &irq); 468 469 /* Only re-route interrupts when necessary. */ 470 if ((sta & STA_ENABLED) && irq._int == newirq) { 471 aml_freevalue(&res); 472 return; 473 } 474 475 crs = (union acpi_resource *)res.v_buffer; 476 switch (AML_CRSTYPE(crs)) { 477 case SR_IRQ: 478 crs->sr_irq.irq_mask = htole16(1 << newirq); 479 break; 480 case LR_EXTIRQ: 481 crs->lr_extirq.irq[0] = htole32(newirq); 482 break; 483 } 484 485 if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) { 486 printf("no _SRS method\n"); 487 aml_freevalue(&res); 488 return; 489 } 490 aml_freevalue(&res); 491 aml_freevalue(&res2); 492 } 493