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