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