1 /* $OpenBSD: acpiprt.c,v 1.51 2021/03/10 21:49:55 patrick 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_NAMEREF) { 276 node = aml_searchrel(sc->sc_devnode, 277 aml_getname(pp->v_nameref)); 278 if (node == NULL) { 279 printf("Invalid device\n"); 280 return; 281 } 282 pp = node->value; 283 } 284 if (pp->type == AML_OBJTYPE_OBJREF) { 285 pp = pp->v_objref.ref; 286 } 287 if (pp->type == AML_OBJTYPE_DEVICE) { 288 node = pp->node; 289 290 sta = acpi_getsta(sc->sc_acpi, node); 291 if ((sta & STA_PRESENT) == 0) 292 return; 293 294 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { 295 printf("no _CRS method\n"); 296 return; 297 } 298 299 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 300 printf("invalid _CRS object\n"); 301 aml_freevalue(&res); 302 return; 303 } 304 aml_parse_resource(&res, acpiprt_getirq, &irq); 305 aml_freevalue(&res); 306 307 /* Pick a new IRQ if necessary. */ 308 if ((irq._int == 0 || irq._int == 2 || irq._int == 13) && 309 !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){ 310 aml_parse_resource(&res, acpiprt_chooseirq, &irq); 311 aml_freevalue(&res); 312 } 313 314 if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL) 315 return; 316 p->bus = sc->sc_bus; 317 p->dev = ACPI_PCI_DEV(addr << 16); 318 p->pin = pin; 319 p->irq = irq._int; 320 p->sc = sc; 321 p->node = node; 322 SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list); 323 } else { 324 irq._int = aml_val2int(v->v_package[3]); 325 irq._shr = 1; 326 irq._ll = 1; 327 irq._he = 0; 328 } 329 330 #ifdef ACPI_DEBUG 331 printf("%s: %s addr 0x%llx pin %d irq %d\n", 332 DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int); 333 #endif 334 335 #if NIOAPIC > 0 336 if (nioapics > 0) { 337 apic = ioapic_find_bybase(irq._int); 338 if (apic == NULL) { 339 printf("%s: no apic found for irq %d\n", 340 DEVNAME(sc), irq._int); 341 return; 342 } 343 344 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO); 345 if (map == NULL) 346 return; 347 348 map->ioapic = apic; 349 map->ioapic_pin = irq._int - apic->sc_apic_vecbase; 350 map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3); 351 if (irq._ll) 352 map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT); 353 else 354 map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT); 355 if (irq._he) 356 map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT); 357 else 358 map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT); 359 360 map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); 361 switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) { 362 case MPS_INTPO_DEF: 363 case MPS_INTPO_ACTLO: 364 map->redir |= IOAPIC_REDLO_ACTLO; 365 break; 366 } 367 switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) { 368 case MPS_INTTR_DEF: 369 case MPS_INTTR_LEVEL: 370 map->redir |= IOAPIC_REDLO_LEVEL; 371 break; 372 } 373 374 map->ioapic_ih = APIC_INT_VIA_APIC | 375 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | 376 (map->ioapic_pin << APIC_INT_PIN_SHIFT)); 377 378 apic->sc_pins[map->ioapic_pin].ip_map = map; 379 380 map->next = mp_busses[sc->sc_bus].mb_intrs; 381 mp_busses[sc->sc_bus].mb_intrs = map; 382 383 return; 384 } 385 #endif 386 387 bus = sc->sc_bus; 388 dev = ACPI_PCI_DEV(addr << 16); 389 tag = pci_make_tag(pc, bus, dev, 0); 390 391 reg = pci_conf_read(pc, tag, PCI_BHLC_REG); 392 if (PCI_HDRTYPE_MULTIFN(reg)) 393 nfuncs = 8; 394 else 395 nfuncs = 1; 396 397 for (func = 0; func < nfuncs; func++) { 398 tag = pci_make_tag(pc, bus, dev, func); 399 reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); 400 if (PCI_INTERRUPT_PIN(reg) == pin + 1) { 401 reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT); 402 reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT; 403 pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg); 404 } 405 } 406 } 407 408 int 409 acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node) 410 { 411 /* Check if parent device has PCI mapping */ 412 return (node->parent && node->parent->pci) ? 413 node->parent->pci->sub : -1; 414 } 415 416 void 417 acpiprt_route_interrupt(int bus, int dev, int pin) 418 { 419 struct acpiprt_softc *sc; 420 struct acpiprt_map *p; 421 struct acpiprt_irq irq; 422 struct aml_node *node = NULL; 423 struct aml_value res, res2; 424 union acpi_resource *crs; 425 int newirq; 426 int64_t sta; 427 428 SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) { 429 if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) { 430 newirq = p->irq; 431 sc = p->sc; 432 node = p->node; 433 break; 434 } 435 } 436 if (node == NULL) 437 return; 438 439 sta = acpi_getsta(sc->sc_acpi, node); 440 KASSERT(sta & STA_PRESENT); 441 442 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { 443 printf("no _CRS method\n"); 444 return; 445 } 446 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 447 printf("invalid _CRS object\n"); 448 aml_freevalue(&res); 449 return; 450 } 451 aml_parse_resource(&res, acpiprt_getirq, &irq); 452 453 /* Only re-route interrupts when necessary. */ 454 if ((sta & STA_ENABLED) && irq._int == newirq) { 455 aml_freevalue(&res); 456 return; 457 } 458 459 crs = (union acpi_resource *)res.v_buffer; 460 switch (AML_CRSTYPE(crs)) { 461 case SR_IRQ: 462 crs->sr_irq.irq_mask = htole16(1 << newirq); 463 break; 464 case LR_EXTIRQ: 465 crs->lr_extirq.irq[0] = htole32(newirq); 466 break; 467 } 468 469 if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) { 470 printf("no _SRS method\n"); 471 aml_freevalue(&res); 472 return; 473 } 474 aml_freevalue(&res); 475 aml_freevalue(&res2); 476 } 477