| 1 | /* |
| 2 | * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.kfreebsd.org> |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 24 | * SUCH DAMAGE. |
| 25 | * |
| 26 | * $FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.56.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $ |
| 27 | */ |
| 28 | |
| 29 | #define MPASS(ex) MPASS4(ex, #ex, __FILE__, __LINE__) |
| 30 | #define MPASS4(ex, what, file, line) \ |
| 31 | KASSERT((ex), ("Assertion %s failed at %s:%d", what, file, line)) |
| 32 | |
| 33 | #include "opt_acpi.h" |
| 34 | #include <sys/param.h> |
| 35 | #include <sys/bus.h> |
| 36 | #include <sys/kernel.h> |
| 37 | #include <sys/limits.h> |
| 38 | #include <sys/malloc.h> |
| 39 | #include <sys/module.h> |
| 40 | |
| 41 | #include "acpi.h" |
| 42 | #include <dev/acpica5/acpivar.h> |
| 43 | #include <dev/acpica5/acpi_pcibvar.h> |
| 44 | |
| 45 | #include <bus/pci/i386/pci_cfgreg.h> |
| 46 | #include <bus/pci/pcireg.h> |
| 47 | #include <bus/pci/pcivar.h> |
| 48 | #include "pcib_if.h" |
| 49 | |
| 50 | /* Hooks for the ACPI CA debugging infrastructure. */ |
| 51 | #define _COMPONENT ACPI_BUS |
| 52 | ACPI_MODULE_NAME("PCI_LINK") |
| 53 | |
| 54 | ACPI_SERIAL_DECL(pci_link, "ACPI PCI link"); |
| 55 | |
| 56 | #define NUM_ISA_INTERRUPTS 16 |
| 57 | #define NUM_ACPI_INTERRUPTS 256 |
| 58 | |
| 59 | /* |
| 60 | * An ACPI PCI link device may contain multiple links. Each link has its |
| 61 | * own ACPI resource. _PRT entries specify which link is being used via |
| 62 | * the Source Index. |
| 63 | * |
| 64 | * XXX: A note about Source Indices and DPFs: Currently we assume that |
| 65 | * the DPF start and end tags are not counted towards the index that |
| 66 | * Source Index corresponds to. Also, we assume that when DPFs are in use |
| 67 | * they various sets overlap in terms of Indices. Here's an example |
| 68 | * resource list indicating these assumptions: |
| 69 | * |
| 70 | * Resource Index |
| 71 | * -------- ----- |
| 72 | * I/O Port 0 |
| 73 | * Start DPF - |
| 74 | * IRQ 1 |
| 75 | * MemIO 2 |
| 76 | * Start DPF - |
| 77 | * IRQ 1 |
| 78 | * MemIO 2 |
| 79 | * End DPF - |
| 80 | * DMA Channel 3 |
| 81 | * |
| 82 | * The XXX is because I'm not sure if this is a valid assumption to make. |
| 83 | */ |
| 84 | |
| 85 | /* States during DPF processing. */ |
| 86 | #define DPF_OUTSIDE 0 |
| 87 | #define DPF_FIRST 1 |
| 88 | #define DPF_IGNORE 2 |
| 89 | |
| 90 | struct link; |
| 91 | |
| 92 | struct acpi_pci_link_softc { |
| 93 | int pl_num_links; |
| 94 | int pl_crs_bad; |
| 95 | struct link *pl_links; |
| 96 | device_t pl_dev; |
| 97 | }; |
| 98 | |
| 99 | struct link { |
| 100 | struct acpi_pci_link_softc *l_sc; |
| 101 | uint8_t l_bios_irq; |
| 102 | uint8_t l_irq; |
| 103 | uint8_t l_initial_irq; |
| 104 | int l_res_index; |
| 105 | int l_num_irqs; |
| 106 | int *l_irqs; |
| 107 | int l_references; |
| 108 | int l_routed:1; |
| 109 | int l_isa_irq:1; |
| 110 | ACPI_RESOURCE l_prs_template; |
| 111 | }; |
| 112 | |
| 113 | struct link_count_request { |
| 114 | int in_dpf; |
| 115 | int count; |
| 116 | }; |
| 117 | |
| 118 | struct link_res_request { |
| 119 | struct acpi_pci_link_softc *sc; |
| 120 | int in_dpf; |
| 121 | int res_index; |
| 122 | int link_index; |
| 123 | }; |
| 124 | |
| 125 | MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures"); |
| 126 | |
| 127 | static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS]; |
| 128 | static int pci_link_bios_isa_irqs; |
| 129 | |
| 130 | static char *pci_link_ids[] = { "PNP0C0F", NULL }; |
| 131 | |
| 132 | /* |
| 133 | * Fetch the short name associated with an ACPI handle and save it in the |
| 134 | * passed in buffer. |
| 135 | */ |
| 136 | static ACPI_STATUS |
| 137 | acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen) |
| 138 | { |
| 139 | ACPI_BUFFER buf; |
| 140 | |
| 141 | buf.Length = buflen; |
| 142 | buf.Pointer = buffer; |
| 143 | return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf)); |
| 144 | } |
| 145 | |
| 146 | static int |
| 147 | acpi_pci_link_probe(device_t dev) |
| 148 | { |
| 149 | char descr[28], name[12]; |
| 150 | |
| 151 | /* |
| 152 | * We explicitly do not check _STA since not all systems set it to |
| 153 | * sensible values. |
| 154 | */ |
| 155 | if (acpi_disabled("pci_link") || |
| 156 | ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids) == NULL) |
| 157 | return (ENXIO); |
| 158 | |
| 159 | if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name, |
| 160 | sizeof(name)))) { |
| 161 | ksnprintf(descr, sizeof(descr), "ACPI PCI Link %s", name); |
| 162 | device_set_desc_copy(dev, descr); |
| 163 | } else |
| 164 | device_set_desc(dev, "ACPI PCI Link"); |
| 165 | device_quiet(dev); |
| 166 | return (0); |
| 167 | } |
| 168 | |
| 169 | static ACPI_STATUS |
| 170 | acpi_count_irq_resources(ACPI_RESOURCE *res, void *context) |
| 171 | { |
| 172 | struct link_count_request *req; |
| 173 | |
| 174 | req = (struct link_count_request *)context; |
| 175 | switch (res->Type) { |
| 176 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: |
| 177 | switch (req->in_dpf) { |
| 178 | case DPF_OUTSIDE: |
| 179 | /* We've started the first DPF. */ |
| 180 | req->in_dpf = DPF_FIRST; |
| 181 | break; |
| 182 | case DPF_FIRST: |
| 183 | /* We've started the second DPF. */ |
| 184 | req->in_dpf = DPF_IGNORE; |
| 185 | break; |
| 186 | } |
| 187 | break; |
| 188 | case ACPI_RESOURCE_TYPE_END_DEPENDENT: |
| 189 | /* We are finished with DPF parsing. */ |
| 190 | KASSERT(req->in_dpf != DPF_OUTSIDE, |
| 191 | ("%s: end dpf when not parsing a dpf", __func__)); |
| 192 | req->in_dpf = DPF_OUTSIDE; |
| 193 | break; |
| 194 | case ACPI_RESOURCE_TYPE_IRQ: |
| 195 | case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: |
| 196 | /* |
| 197 | * Don't count resources if we are in a DPF set that we are |
| 198 | * ignoring. |
| 199 | */ |
| 200 | if (req->in_dpf != DPF_IGNORE) |
| 201 | req->count++; |
| 202 | } |
| 203 | return (AE_OK); |
| 204 | } |
| 205 | |
| 206 | static ACPI_STATUS |
| 207 | link_add_crs(ACPI_RESOURCE *res, void *context) |
| 208 | { |
| 209 | struct link_res_request *req; |
| 210 | struct link *link; |
| 211 | |
| 212 | ACPI_SERIAL_ASSERT(pci_link); |
| 213 | req = (struct link_res_request *)context; |
| 214 | switch (res->Type) { |
| 215 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: |
| 216 | switch (req->in_dpf) { |
| 217 | case DPF_OUTSIDE: |
| 218 | /* We've started the first DPF. */ |
| 219 | req->in_dpf = DPF_FIRST; |
| 220 | break; |
| 221 | case DPF_FIRST: |
| 222 | /* We've started the second DPF. */ |
| 223 | panic( |
| 224 | "%s: Multiple dependent functions within a current resource", |
| 225 | __func__); |
| 226 | break; |
| 227 | } |
| 228 | break; |
| 229 | case ACPI_RESOURCE_TYPE_END_DEPENDENT: |
| 230 | /* We are finished with DPF parsing. */ |
| 231 | KASSERT(req->in_dpf != DPF_OUTSIDE, |
| 232 | ("%s: end dpf when not parsing a dpf", __func__)); |
| 233 | req->in_dpf = DPF_OUTSIDE; |
| 234 | break; |
| 235 | case ACPI_RESOURCE_TYPE_IRQ: |
| 236 | case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: |
| 237 | KASSERT(req->link_index < req->sc->pl_num_links, |
| 238 | ("%s: array boundary violation", __func__)); |
| 239 | link = &req->sc->pl_links[req->link_index]; |
| 240 | link->l_res_index = req->res_index; |
| 241 | req->link_index++; |
| 242 | req->res_index++; |
| 243 | |
| 244 | /* |
| 245 | * Only use the current value if there's one IRQ. Some |
| 246 | * systems return multiple IRQs (which is nonsense for _CRS) |
| 247 | * when the link hasn't been programmed. |
| 248 | */ |
| 249 | if (res->Type == ACPI_RESOURCE_TYPE_IRQ) { |
| 250 | if (res->Data.Irq.InterruptCount == 1) |
| 251 | link->l_irq = res->Data.Irq.Interrupts[0]; |
| 252 | } else if (res->Data.ExtendedIrq.InterruptCount == 1) |
| 253 | link->l_irq = res->Data.ExtendedIrq.Interrupts[0]; |
| 254 | |
| 255 | /* |
| 256 | * An IRQ of zero means that the link isn't routed. |
| 257 | */ |
| 258 | if (link->l_irq == 0) |
| 259 | link->l_irq = PCI_INVALID_IRQ; |
| 260 | break; |
| 261 | default: |
| 262 | req->res_index++; |
| 263 | } |
| 264 | return (AE_OK); |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | * Populate the set of possible IRQs for each device. |
| 269 | */ |
| 270 | static ACPI_STATUS |
| 271 | link_add_prs(ACPI_RESOURCE *res, void *context) |
| 272 | { |
| 273 | struct link_res_request *req; |
| 274 | struct link *link; |
| 275 | UINT8 *irqs = NULL; |
| 276 | UINT32 *ext_irqs = NULL; |
| 277 | int i, is_ext_irq = 1; |
| 278 | |
| 279 | ACPI_SERIAL_ASSERT(pci_link); |
| 280 | req = (struct link_res_request *)context; |
| 281 | switch (res->Type) { |
| 282 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: |
| 283 | switch (req->in_dpf) { |
| 284 | case DPF_OUTSIDE: |
| 285 | /* We've started the first DPF. */ |
| 286 | req->in_dpf = DPF_FIRST; |
| 287 | break; |
| 288 | case DPF_FIRST: |
| 289 | /* We've started the second DPF. */ |
| 290 | req->in_dpf = DPF_IGNORE; |
| 291 | break; |
| 292 | } |
| 293 | break; |
| 294 | case ACPI_RESOURCE_TYPE_END_DEPENDENT: |
| 295 | /* We are finished with DPF parsing. */ |
| 296 | KASSERT(req->in_dpf != DPF_OUTSIDE, |
| 297 | ("%s: end dpf when not parsing a dpf", __func__)); |
| 298 | req->in_dpf = DPF_OUTSIDE; |
| 299 | break; |
| 300 | case ACPI_RESOURCE_TYPE_IRQ: |
| 301 | is_ext_irq = 0; |
| 302 | /* fall through */ |
| 303 | case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: |
| 304 | /* |
| 305 | * Don't parse resources if we are in a DPF set that we are |
| 306 | * ignoring. |
| 307 | */ |
| 308 | if (req->in_dpf == DPF_IGNORE) |
| 309 | break; |
| 310 | |
| 311 | KASSERT(req->link_index < req->sc->pl_num_links, |
| 312 | ("%s: array boundary violation", __func__)); |
| 313 | link = &req->sc->pl_links[req->link_index]; |
| 314 | if (link->l_res_index == -1) { |
| 315 | KASSERT(req->sc->pl_crs_bad, |
| 316 | ("res_index should be set")); |
| 317 | link->l_res_index = req->res_index; |
| 318 | } |
| 319 | req->link_index++; |
| 320 | req->res_index++; |
| 321 | |
| 322 | /* |
| 323 | * Stash a copy of the resource for later use when doing |
| 324 | * _SRS. |
| 325 | */ |
| 326 | bcopy(res, &link->l_prs_template, sizeof(ACPI_RESOURCE)); |
| 327 | if (is_ext_irq) { |
| 328 | link->l_num_irqs = |
| 329 | res->Data.ExtendedIrq.InterruptCount; |
| 330 | ext_irqs = res->Data.ExtendedIrq.Interrupts; |
| 331 | } else { |
| 332 | link->l_num_irqs = res->Data.Irq.InterruptCount; |
| 333 | irqs = res->Data.Irq.Interrupts; |
| 334 | } |
| 335 | if (link->l_num_irqs == 0) |
| 336 | break; |
| 337 | |
| 338 | /* |
| 339 | * Save a list of the valid IRQs. Also, if all of the |
| 340 | * valid IRQs are ISA IRQs, then mark this link as |
| 341 | * routed via an ISA interrupt. |
| 342 | */ |
| 343 | link->l_isa_irq = TRUE; |
| 344 | |
| 345 | link->l_irqs = kmalloc(sizeof(int) * link->l_num_irqs, |
| 346 | M_PCI_LINK, M_WAITOK | M_ZERO); |
| 347 | for (i = 0; i < link->l_num_irqs; i++) { |
| 348 | if (is_ext_irq) { |
| 349 | link->l_irqs[i] = ext_irqs[i]; |
| 350 | if (ext_irqs[i] >= NUM_ISA_INTERRUPTS) |
| 351 | link->l_isa_irq = FALSE; |
| 352 | } else { |
| 353 | link->l_irqs[i] = irqs[i]; |
| 354 | if (irqs[i] >= NUM_ISA_INTERRUPTS) |
| 355 | link->l_isa_irq = FALSE; |
| 356 | } |
| 357 | } |
| 358 | break; |
| 359 | default: |
| 360 | if (req->in_dpf == DPF_IGNORE) |
| 361 | break; |
| 362 | if (req->sc->pl_crs_bad) |
| 363 | device_printf(req->sc->pl_dev, |
| 364 | "Warning: possible resource %d will be lost during _SRS\n", |
| 365 | req->res_index); |
| 366 | req->res_index++; |
| 367 | } |
| 368 | return (AE_OK); |
| 369 | } |
| 370 | |
| 371 | static int |
| 372 | link_valid_irq(struct link *link, int irq) |
| 373 | { |
| 374 | int i; |
| 375 | |
| 376 | ACPI_SERIAL_ASSERT(pci_link); |
| 377 | |
| 378 | /* Invalid interrupts are never valid. */ |
| 379 | if (!PCI_INTERRUPT_VALID(irq)) |
| 380 | return (FALSE); |
| 381 | |
| 382 | /* Any interrupt in the list of possible interrupts is valid. */ |
| 383 | for (i = 0; i < link->l_num_irqs; i++) |
| 384 | if (link->l_irqs[i] == irq) |
| 385 | return (TRUE); |
| 386 | |
| 387 | /* |
| 388 | * For links routed via an ISA interrupt, if the SCI is routed via |
| 389 | * an ISA interrupt, the SCI is always treated as a valid IRQ. |
| 390 | */ |
| 391 | if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq && |
| 392 | irq < NUM_ISA_INTERRUPTS) |
| 393 | return (TRUE); |
| 394 | |
| 395 | /* If the interrupt wasn't found in the list it is not valid. */ |
| 396 | return (FALSE); |
| 397 | } |
| 398 | |
| 399 | static void |
| 400 | acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag) |
| 401 | { |
| 402 | struct link *link; |
| 403 | char buf[16]; |
| 404 | int i, j; |
| 405 | |
| 406 | ACPI_SERIAL_ASSERT(pci_link); |
| 407 | if (header) { |
| 408 | ksnprintf(buf, sizeof(buf), "%s:", |
| 409 | device_get_nameunit(sc->pl_dev)); |
| 410 | kprintf("%-16.16s Index IRQ Rtd Ref IRQs\n", buf); |
| 411 | } |
| 412 | for (i = 0; i < sc->pl_num_links; i++) { |
| 413 | link = &sc->pl_links[i]; |
| 414 | kprintf(" %-14.14s %5d %3d %c %3d ", i == 0 ? tag : "", i, |
| 415 | link->l_irq, link->l_routed ? 'Y' : 'N', |
| 416 | link->l_references); |
| 417 | if (link->l_num_irqs == 0) |
| 418 | kprintf(" none"); |
| 419 | else for (j = 0; j < link->l_num_irqs; j++) |
| 420 | kprintf(" %d", link->l_irqs[j]); |
| 421 | kprintf("\n"); |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | static int |
| 426 | acpi_pci_link_attach(device_t dev) |
| 427 | { |
| 428 | struct acpi_pci_link_softc *sc; |
| 429 | struct link_count_request creq; |
| 430 | struct link_res_request rreq; |
| 431 | ACPI_STATUS status; |
| 432 | int i; |
| 433 | |
| 434 | sc = device_get_softc(dev); |
| 435 | sc->pl_dev = dev; |
| 436 | ACPI_SERIAL_INIT(pci_link); |
| 437 | ACPI_SERIAL_BEGIN(pci_link); |
| 438 | |
| 439 | /* |
| 440 | * Count the number of current resources so we know how big of |
| 441 | * a link array to allocate. On some systems, _CRS is broken, |
| 442 | * so for those systems try to derive the count from _PRS instead. |
| 443 | */ |
| 444 | creq.in_dpf = DPF_OUTSIDE; |
| 445 | creq.count = 0; |
| 446 | status = AcpiWalkResources(acpi_get_handle(dev), "_CRS", |
| 447 | acpi_count_irq_resources, &creq); |
| 448 | sc->pl_crs_bad = ACPI_FAILURE(status); |
| 449 | if (sc->pl_crs_bad) { |
| 450 | creq.in_dpf = DPF_OUTSIDE; |
| 451 | creq.count = 0; |
| 452 | status = AcpiWalkResources(acpi_get_handle(dev), "_PRS", |
| 453 | acpi_count_irq_resources, &creq); |
| 454 | if (ACPI_FAILURE(status)) { |
| 455 | device_printf(dev, |
| 456 | "Unable to parse _CRS or _PRS: %s\n", |
| 457 | AcpiFormatException(status)); |
| 458 | ACPI_SERIAL_END(pci_link); |
| 459 | return (ENXIO); |
| 460 | } |
| 461 | } |
| 462 | sc->pl_num_links = creq.count; |
| 463 | if (creq.count == 0) { |
| 464 | ACPI_SERIAL_END(pci_link); |
| 465 | return (0); |
| 466 | } |
| 467 | sc->pl_links = kmalloc(sizeof(struct link) * sc->pl_num_links, |
| 468 | M_PCI_LINK, M_WAITOK | M_ZERO); |
| 469 | |
| 470 | /* Initialize the child links. */ |
| 471 | for (i = 0; i < sc->pl_num_links; i++) { |
| 472 | sc->pl_links[i].l_irq = PCI_INVALID_IRQ; |
| 473 | sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ; |
| 474 | sc->pl_links[i].l_sc = sc; |
| 475 | sc->pl_links[i].l_isa_irq = FALSE; |
| 476 | sc->pl_links[i].l_res_index = -1; |
| 477 | } |
| 478 | |
| 479 | /* Try to read the current settings from _CRS if it is valid. */ |
| 480 | if (!sc->pl_crs_bad) { |
| 481 | rreq.in_dpf = DPF_OUTSIDE; |
| 482 | rreq.link_index = 0; |
| 483 | rreq.res_index = 0; |
| 484 | rreq.sc = sc; |
| 485 | status = AcpiWalkResources(acpi_get_handle(dev), "_CRS", |
| 486 | link_add_crs, &rreq); |
| 487 | if (ACPI_FAILURE(status)) { |
| 488 | device_printf(dev, "Unable to parse _CRS: %s\n", |
| 489 | AcpiFormatException(status)); |
| 490 | goto fail; |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | /* |
| 495 | * Try to read the possible settings from _PRS. Note that if the |
| 496 | * _CRS is toast, we depend on having a working _PRS. However, if |
| 497 | * _CRS works, then it is ok for _PRS to be missing. |
| 498 | */ |
| 499 | rreq.in_dpf = DPF_OUTSIDE; |
| 500 | rreq.link_index = 0; |
| 501 | rreq.res_index = 0; |
| 502 | rreq.sc = sc; |
| 503 | status = AcpiWalkResources(acpi_get_handle(dev), "_PRS", |
| 504 | link_add_prs, &rreq); |
| 505 | if (ACPI_FAILURE(status) && |
| 506 | (status != AE_NOT_FOUND || sc->pl_crs_bad)) { |
| 507 | device_printf(dev, "Unable to parse _PRS: %s\n", |
| 508 | AcpiFormatException(status)); |
| 509 | goto fail; |
| 510 | } |
| 511 | if (bootverbose) |
| 512 | acpi_pci_link_dump(sc, 1, "Initial Probe"); |
| 513 | |
| 514 | /* Verify initial IRQs if we have _PRS. */ |
| 515 | if (status != AE_NOT_FOUND) |
| 516 | for (i = 0; i < sc->pl_num_links; i++) |
| 517 | if (!link_valid_irq(&sc->pl_links[i], |
| 518 | sc->pl_links[i].l_irq)) |
| 519 | sc->pl_links[i].l_irq = PCI_INVALID_IRQ; |
| 520 | if (bootverbose) |
| 521 | acpi_pci_link_dump(sc, 0, "Validation"); |
| 522 | |
| 523 | /* Save initial IRQs. */ |
| 524 | for (i = 0; i < sc->pl_num_links; i++) |
| 525 | sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq; |
| 526 | |
| 527 | /* |
| 528 | * Try to disable this link. If successful, set the current IRQ to |
| 529 | * zero and flags to indicate this link is not routed. If we can't |
| 530 | * run _DIS (i.e., the method doesn't exist), assume the initial |
| 531 | * IRQ was routed by the BIOS. |
| 532 | */ |
| 533 | if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, |
| 534 | NULL))) |
| 535 | for (i = 0; i < sc->pl_num_links; i++) |
| 536 | sc->pl_links[i].l_irq = PCI_INVALID_IRQ; |
| 537 | else |
| 538 | for (i = 0; i < sc->pl_num_links; i++) |
| 539 | if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq)) |
| 540 | sc->pl_links[i].l_routed = TRUE; |
| 541 | if (bootverbose) |
| 542 | acpi_pci_link_dump(sc, 0, "After Disable"); |
| 543 | ACPI_SERIAL_END(pci_link); |
| 544 | return (0); |
| 545 | fail: |
| 546 | ACPI_SERIAL_END(pci_link); |
| 547 | for (i = 0; i < sc->pl_num_links; i++) |
| 548 | if (sc->pl_links[i].l_irqs != NULL) |
| 549 | kfree(sc->pl_links[i].l_irqs, M_PCI_LINK); |
| 550 | kfree(sc->pl_links, M_PCI_LINK); |
| 551 | return (ENXIO); |
| 552 | } |
| 553 | |
| 554 | /* XXX: Note that this is identical to pci_pir_search_irq(). */ |
| 555 | static uint8_t |
| 556 | acpi_pci_link_search_irq(int bus, int device, int pin) |
| 557 | { |
| 558 | uint32_t value; |
| 559 | uint8_t func, maxfunc; |
| 560 | |
| 561 | /* See if we have a valid device at function 0. */ |
| 562 | value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1); |
| 563 | if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) |
| 564 | return (PCI_INVALID_IRQ); |
| 565 | if (value & PCIM_MFDEV) |
| 566 | maxfunc = PCI_FUNCMAX; |
| 567 | else |
| 568 | maxfunc = 0; |
| 569 | |
| 570 | /* Scan all possible functions at this device. */ |
| 571 | for (func = 0; func <= maxfunc; func++) { |
| 572 | value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4); |
| 573 | if (value == 0xffffffff) |
| 574 | continue; |
| 575 | value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1); |
| 576 | |
| 577 | /* |
| 578 | * See if it uses the pin in question. Note that the passed |
| 579 | * in pin uses 0 for A, .. 3 for D whereas the intpin |
| 580 | * register uses 0 for no interrupt, 1 for A, .. 4 for D. |
| 581 | */ |
| 582 | if (value != pin + 1) |
| 583 | continue; |
| 584 | value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1); |
| 585 | if (bootverbose) |
| 586 | kprintf( |
| 587 | "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n", |
| 588 | bus, device, pin + 'A', func, value); |
| 589 | if (value != PCI_INVALID_IRQ) |
| 590 | return (value); |
| 591 | } |
| 592 | return (PCI_INVALID_IRQ); |
| 593 | } |
| 594 | |
| 595 | /* |
| 596 | * Find the link structure that corresponds to the resource index passed in |
| 597 | * via 'source_index'. |
| 598 | */ |
| 599 | static struct link * |
| 600 | acpi_pci_link_lookup(device_t dev, int source_index) |
| 601 | { |
| 602 | struct acpi_pci_link_softc *sc; |
| 603 | int i; |
| 604 | |
| 605 | ACPI_SERIAL_ASSERT(pci_link); |
| 606 | sc = device_get_softc(dev); |
| 607 | for (i = 0; i < sc->pl_num_links; i++) |
| 608 | if (sc->pl_links[i].l_res_index == source_index) |
| 609 | return (&sc->pl_links[i]); |
| 610 | return (NULL); |
| 611 | } |
| 612 | |
| 613 | void |
| 614 | acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot, |
| 615 | int pin) |
| 616 | { |
| 617 | struct link *link; |
| 618 | uint8_t bios_irq; |
| 619 | uintptr_t bus; |
| 620 | |
| 621 | /* |
| 622 | * Look up the PCI bus for the specified PCI bridge device. Note |
| 623 | * that the PCI bridge device might not have any children yet. |
| 624 | * However, looking up its bus number doesn't require a valid child |
| 625 | * device, so we just pass NULL. |
| 626 | */ |
| 627 | if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) { |
| 628 | device_printf(pcib, "Unable to read PCI bus number"); |
| 629 | panic("PCI bridge without a bus number"); |
| 630 | } |
| 631 | |
| 632 | /* Bump the reference count. */ |
| 633 | ACPI_SERIAL_BEGIN(pci_link); |
| 634 | link = acpi_pci_link_lookup(dev, index); |
| 635 | if (link == NULL) { |
| 636 | device_printf(dev, "apparently invalid index %d\n", index); |
| 637 | ACPI_SERIAL_END(pci_link); |
| 638 | return; |
| 639 | } |
| 640 | link->l_references++; |
| 641 | if (link->l_routed) |
| 642 | pci_link_interrupt_weights[link->l_irq]++; |
| 643 | |
| 644 | /* |
| 645 | * The BIOS only routes interrupts via ISA IRQs using the ATPICs |
| 646 | * (8259As). Thus, if this link is routed via an ISA IRQ, go |
| 647 | * look to see if the BIOS routed an IRQ for this link at the |
| 648 | * indicated (bus, slot, pin). If so, we prefer that IRQ for |
| 649 | * this link and add that IRQ to our list of known-good IRQs. |
| 650 | * This provides a good work-around for link devices whose _CRS |
| 651 | * method is either broken or bogus. We only use the value |
| 652 | * returned by _CRS if we can't find a valid IRQ via this method |
| 653 | * in fact. |
| 654 | * |
| 655 | * If this link is not routed via an ISA IRQ (because we are using |
| 656 | * APIC for example), then don't bother looking up the BIOS IRQ |
| 657 | * as if we find one it won't be valid anyway. |
| 658 | */ |
| 659 | if (!link->l_isa_irq) { |
| 660 | ACPI_SERIAL_END(pci_link); |
| 661 | return; |
| 662 | } |
| 663 | |
| 664 | /* Try to find a BIOS IRQ setting from any matching devices. */ |
| 665 | bios_irq = acpi_pci_link_search_irq(bus, slot, pin); |
| 666 | if (!PCI_INTERRUPT_VALID(bios_irq)) { |
| 667 | ACPI_SERIAL_END(pci_link); |
| 668 | return; |
| 669 | } |
| 670 | |
| 671 | /* Validate the BIOS IRQ. */ |
| 672 | if (!link_valid_irq(link, bios_irq)) { |
| 673 | device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n", |
| 674 | bios_irq, (int)bus, slot, pin + 'A'); |
| 675 | } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) { |
| 676 | link->l_bios_irq = bios_irq; |
| 677 | /* |
| 678 | * Avoid sharing interrupt with SCI if possible, |
| 679 | * since ACPI interrupt handler is generally time |
| 680 | * consuming. |
| 681 | */ |
| 682 | if (bios_irq < NUM_ISA_INTERRUPTS && |
| 683 | AcpiGbl_FADT.SciInterrupt != bios_irq) |
| 684 | pci_link_bios_isa_irqs |= (1 << bios_irq); |
| 685 | if (bios_irq != link->l_initial_irq && |
| 686 | PCI_INTERRUPT_VALID(link->l_initial_irq)) |
| 687 | device_printf(dev, |
| 688 | "BIOS IRQ %u does not match initial IRQ %u\n", |
| 689 | bios_irq, link->l_initial_irq); |
| 690 | } else if (bios_irq != link->l_bios_irq) |
| 691 | device_printf(dev, |
| 692 | "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n", |
| 693 | bios_irq, (int)bus, slot, pin + 'A', |
| 694 | link->l_bios_irq); |
| 695 | ACPI_SERIAL_END(pci_link); |
| 696 | } |
| 697 | |
| 698 | static ACPI_STATUS |
| 699 | acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf) |
| 700 | { |
| 701 | ACPI_RESOURCE *resource, *end, newres, *resptr; |
| 702 | ACPI_BUFFER crsbuf; |
| 703 | ACPI_STATUS status; |
| 704 | struct link *link; |
| 705 | int i, in_dpf; |
| 706 | |
| 707 | /* Fetch the _CRS. */ |
| 708 | ACPI_SERIAL_ASSERT(pci_link); |
| 709 | crsbuf.Pointer = NULL; |
| 710 | crsbuf.Length = ACPI_ALLOCATE_BUFFER; |
| 711 | status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), &crsbuf); |
| 712 | if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL) |
| 713 | status = AE_NO_MEMORY; |
| 714 | if (ACPI_FAILURE(status)) { |
| 715 | if (bootverbose) |
| 716 | device_printf(sc->pl_dev, |
| 717 | "Unable to fetch current resources: %s\n", |
| 718 | AcpiFormatException(status)); |
| 719 | return (status); |
| 720 | } |
| 721 | |
| 722 | /* Fill in IRQ resources via link structures. */ |
| 723 | srsbuf->Pointer = NULL; |
| 724 | link = sc->pl_links; |
| 725 | i = 0; |
| 726 | in_dpf = DPF_OUTSIDE; |
| 727 | resource = (ACPI_RESOURCE *)crsbuf.Pointer; |
| 728 | end = (ACPI_RESOURCE *)((char *)crsbuf.Pointer + crsbuf.Length); |
| 729 | for (;;) { |
| 730 | switch (resource->Type) { |
| 731 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: |
| 732 | switch (in_dpf) { |
| 733 | case DPF_OUTSIDE: |
| 734 | /* We've started the first DPF. */ |
| 735 | in_dpf = DPF_FIRST; |
| 736 | break; |
| 737 | case DPF_FIRST: |
| 738 | /* We've started the second DPF. */ |
| 739 | panic( |
| 740 | "%s: Multiple dependent functions within a current resource", |
| 741 | __func__); |
| 742 | break; |
| 743 | } |
| 744 | resptr = NULL; |
| 745 | break; |
| 746 | case ACPI_RESOURCE_TYPE_END_DEPENDENT: |
| 747 | /* We are finished with DPF parsing. */ |
| 748 | KASSERT(in_dpf != DPF_OUTSIDE, |
| 749 | ("%s: end dpf when not parsing a dpf", __func__)); |
| 750 | in_dpf = DPF_OUTSIDE; |
| 751 | resptr = NULL; |
| 752 | break; |
| 753 | case ACPI_RESOURCE_TYPE_IRQ: |
| 754 | MPASS(i < sc->pl_num_links); |
| 755 | MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ); |
| 756 | newres = link->l_prs_template; |
| 757 | resptr = &newres; |
| 758 | resptr->Data.Irq.InterruptCount = 1; |
| 759 | if (PCI_INTERRUPT_VALID(link->l_irq)) { |
| 760 | KASSERT(link->l_irq < NUM_ISA_INTERRUPTS, |
| 761 | ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type", |
| 762 | __func__, link->l_irq)); |
| 763 | resptr->Data.Irq.Interrupts[0] = link->l_irq; |
| 764 | } else |
| 765 | resptr->Data.Irq.Interrupts[0] = 0; |
| 766 | link++; |
| 767 | i++; |
| 768 | break; |
| 769 | case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: |
| 770 | MPASS(i < sc->pl_num_links); |
| 771 | MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ); |
| 772 | newres = link->l_prs_template; |
| 773 | resptr = &newres; |
| 774 | resptr->Data.ExtendedIrq.InterruptCount = 1; |
| 775 | if (PCI_INTERRUPT_VALID(link->l_irq)) |
| 776 | resptr->Data.ExtendedIrq.Interrupts[0] = |
| 777 | link->l_irq; |
| 778 | else |
| 779 | resptr->Data.ExtendedIrq.Interrupts[0] = 0; |
| 780 | link++; |
| 781 | i++; |
| 782 | break; |
| 783 | default: |
| 784 | resptr = resource; |
| 785 | } |
| 786 | if (resptr != NULL) { |
| 787 | status = acpi_AppendBufferResource(srsbuf, resptr); |
| 788 | if (ACPI_FAILURE(status)) { |
| 789 | device_printf(sc->pl_dev, |
| 790 | "Unable to build resources: %s\n", |
| 791 | AcpiFormatException(status)); |
| 792 | if (srsbuf->Pointer != NULL) |
| 793 | AcpiOsFree(srsbuf->Pointer); |
| 794 | AcpiOsFree(crsbuf.Pointer); |
| 795 | return (status); |
| 796 | } |
| 797 | } |
| 798 | if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG) |
| 799 | break; |
| 800 | resource = ACPI_NEXT_RESOURCE(resource); |
| 801 | if (resource >= end) |
| 802 | break; |
| 803 | } |
| 804 | AcpiOsFree(crsbuf.Pointer); |
| 805 | return (AE_OK); |
| 806 | } |
| 807 | |
| 808 | static ACPI_STATUS |
| 809 | acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc, |
| 810 | ACPI_BUFFER *srsbuf) |
| 811 | { |
| 812 | ACPI_RESOURCE newres; |
| 813 | ACPI_STATUS status; |
| 814 | struct link *link; |
| 815 | int i; |
| 816 | |
| 817 | /* Start off with an empty buffer. */ |
| 818 | srsbuf->Pointer = NULL; |
| 819 | link = sc->pl_links; |
| 820 | for (i = 0; i < sc->pl_num_links; i++) { |
| 821 | |
| 822 | /* Add a new IRQ resource from each link. */ |
| 823 | link = &sc->pl_links[i]; |
| 824 | newres = link->l_prs_template; |
| 825 | if (newres.Type == ACPI_RESOURCE_TYPE_IRQ) { |
| 826 | |
| 827 | /* Build an IRQ resource. */ |
| 828 | newres.Data.Irq.InterruptCount = 1; |
| 829 | if (PCI_INTERRUPT_VALID(link->l_irq)) { |
| 830 | KASSERT(link->l_irq < NUM_ISA_INTERRUPTS, |
| 831 | ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type", |
| 832 | __func__, link->l_irq)); |
| 833 | newres.Data.Irq.Interrupts[0] = link->l_irq; |
| 834 | } else |
| 835 | newres.Data.Irq.Interrupts[0] = 0; |
| 836 | } else { |
| 837 | |
| 838 | /* Build an ExtIRQ resuorce. */ |
| 839 | newres.Data.ExtendedIrq.InterruptCount = 1; |
| 840 | if (PCI_INTERRUPT_VALID(link->l_irq)) |
| 841 | newres.Data.ExtendedIrq.Interrupts[0] = |
| 842 | link->l_irq; |
| 843 | else |
| 844 | newres.Data.ExtendedIrq.Interrupts[0] = 0; |
| 845 | } |
| 846 | |
| 847 | /* Add the new resource to the end of the _SRS buffer. */ |
| 848 | status = acpi_AppendBufferResource(srsbuf, &newres); |
| 849 | if (ACPI_FAILURE(status)) { |
| 850 | device_printf(sc->pl_dev, |
| 851 | "Unable to build resources: %s\n", |
| 852 | AcpiFormatException(status)); |
| 853 | if (srsbuf->Pointer != NULL) |
| 854 | AcpiOsFree(srsbuf->Pointer); |
| 855 | return (status); |
| 856 | } |
| 857 | } |
| 858 | return (AE_OK); |
| 859 | } |
| 860 | |
| 861 | static ACPI_STATUS |
| 862 | acpi_pci_link_route_irqs(device_t dev) |
| 863 | { |
| 864 | struct acpi_pci_link_softc *sc; |
| 865 | ACPI_RESOURCE *resource, *end; |
| 866 | ACPI_BUFFER srsbuf; |
| 867 | ACPI_STATUS status; |
| 868 | struct link *link; |
| 869 | int i; |
| 870 | |
| 871 | ACPI_SERIAL_ASSERT(pci_link); |
| 872 | sc = device_get_softc(dev); |
| 873 | if (sc->pl_crs_bad) |
| 874 | status = acpi_pci_link_srs_from_links(sc, &srsbuf); |
| 875 | else |
| 876 | status = acpi_pci_link_srs_from_crs(sc, &srsbuf); |
| 877 | |
| 878 | /* Write out new resources via _SRS. */ |
| 879 | status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf); |
| 880 | if (ACPI_FAILURE(status)) { |
| 881 | device_printf(dev, "Unable to route IRQs: %s\n", |
| 882 | AcpiFormatException(status)); |
| 883 | AcpiOsFree(srsbuf.Pointer); |
| 884 | return (status); |
| 885 | } |
| 886 | |
| 887 | /* |
| 888 | * Perform acpi_config_intr() on each IRQ resource if it was just |
| 889 | * routed for the first time. |
| 890 | */ |
| 891 | link = sc->pl_links; |
| 892 | i = 0; |
| 893 | resource = (ACPI_RESOURCE *)srsbuf.Pointer; |
| 894 | end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length); |
| 895 | for (;;) { |
| 896 | if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG) |
| 897 | break; |
| 898 | switch (resource->Type) { |
| 899 | case ACPI_RESOURCE_TYPE_IRQ: |
| 900 | case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: |
| 901 | MPASS(i < sc->pl_num_links); |
| 902 | |
| 903 | /* |
| 904 | * Only configure the interrupt and update the |
| 905 | * weights if this link has a valid IRQ and was |
| 906 | * previously unrouted. |
| 907 | */ |
| 908 | if (!link->l_routed && |
| 909 | PCI_INTERRUPT_VALID(link->l_irq)) { |
| 910 | link->l_routed = TRUE; |
| 911 | acpi_config_intr(dev, resource); |
| 912 | pci_link_interrupt_weights[link->l_irq] += |
| 913 | link->l_references; |
| 914 | } |
| 915 | link++; |
| 916 | i++; |
| 917 | break; |
| 918 | } |
| 919 | resource = ACPI_NEXT_RESOURCE(resource); |
| 920 | if (resource >= end) |
| 921 | break; |
| 922 | } |
| 923 | AcpiOsFree(srsbuf.Pointer); |
| 924 | return (AE_OK); |
| 925 | } |
| 926 | |
| 927 | static int |
| 928 | acpi_pci_link_resume(device_t dev) |
| 929 | { |
| 930 | struct acpi_pci_link_softc *sc; |
| 931 | ACPI_STATUS status; |
| 932 | int i, routed; |
| 933 | |
| 934 | /* |
| 935 | * If all of our links are routed, then restore the link via _SRS, |
| 936 | * otherwise, disable the link via _DIS. |
| 937 | */ |
| 938 | ACPI_SERIAL_BEGIN(pci_link); |
| 939 | sc = device_get_softc(dev); |
| 940 | routed = 0; |
| 941 | for (i = 0; i < sc->pl_num_links; i++) |
| 942 | if (sc->pl_links[i].l_routed) |
| 943 | routed++; |
| 944 | if (routed == sc->pl_num_links) |
| 945 | status = acpi_pci_link_route_irqs(dev); |
| 946 | else { |
| 947 | AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL); |
| 948 | status = AE_OK; |
| 949 | } |
| 950 | ACPI_SERIAL_END(pci_link); |
| 951 | if (ACPI_FAILURE(status)) |
| 952 | return (ENXIO); |
| 953 | else |
| 954 | return (0); |
| 955 | } |
| 956 | |
| 957 | /* |
| 958 | * Pick an IRQ to use for this unrouted link. |
| 959 | */ |
| 960 | static uint8_t |
| 961 | acpi_pci_link_choose_irq(device_t dev, struct link *link) |
| 962 | { |
| 963 | char tunable_buffer[64], link_name[5]; |
| 964 | u_int8_t best_irq, pos_irq; |
| 965 | int best_weight, pos_weight, i; |
| 966 | |
| 967 | KASSERT(!link->l_routed, ("%s: link already routed", __func__)); |
| 968 | KASSERT(!PCI_INTERRUPT_VALID(link->l_irq), |
| 969 | ("%s: link already has an IRQ", __func__)); |
| 970 | |
| 971 | /* Check for a tunable override. */ |
| 972 | if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name, |
| 973 | sizeof(link_name)))) { |
| 974 | ksnprintf(tunable_buffer, sizeof(tunable_buffer), |
| 975 | "hw.pci.link.%s.%d.irq", link_name, link->l_res_index); |
| 976 | if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) { |
| 977 | if (!link_valid_irq(link, i)) |
| 978 | device_printf(dev, |
| 979 | "Warning, IRQ %d is not listed as valid\n", |
| 980 | i); |
| 981 | return (i); |
| 982 | } |
| 983 | ksnprintf(tunable_buffer, sizeof(tunable_buffer), |
| 984 | "hw.pci.link.%s.irq", link_name); |
| 985 | if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) { |
| 986 | if (!link_valid_irq(link, i)) |
| 987 | device_printf(dev, |
| 988 | "Warning, IRQ %d is not listed as valid\n", |
| 989 | i); |
| 990 | return (i); |
| 991 | } |
| 992 | } |
| 993 | |
| 994 | /* |
| 995 | * If we have a valid BIOS IRQ, use that. We trust what the BIOS |
| 996 | * says it routed over what _CRS says the link thinks is routed. |
| 997 | */ |
| 998 | if (PCI_INTERRUPT_VALID(link->l_bios_irq)) |
| 999 | return (link->l_bios_irq); |
| 1000 | |
| 1001 | /* |
| 1002 | * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS, |
| 1003 | * then use that. |
| 1004 | */ |
| 1005 | if (PCI_INTERRUPT_VALID(link->l_initial_irq)) |
| 1006 | return (link->l_initial_irq); |
| 1007 | |
| 1008 | /* |
| 1009 | * Ok, we have no useful hints, so we have to pick from the |
| 1010 | * possible IRQs. For ISA IRQs we only use interrupts that |
| 1011 | * have already been used by the BIOS. |
| 1012 | */ |
| 1013 | best_irq = PCI_INVALID_IRQ; |
| 1014 | best_weight = INT_MAX; |
| 1015 | for (i = 0; i < link->l_num_irqs; i++) { |
| 1016 | pos_irq = link->l_irqs[i]; |
| 1017 | if (pos_irq < NUM_ISA_INTERRUPTS && |
| 1018 | (pci_link_bios_isa_irqs & 1 << pos_irq) == 0) |
| 1019 | continue; |
| 1020 | pos_weight = pci_link_interrupt_weights[pos_irq]; |
| 1021 | if (pos_weight < best_weight) { |
| 1022 | best_weight = pos_weight; |
| 1023 | best_irq = pos_irq; |
| 1024 | } |
| 1025 | } |
| 1026 | |
| 1027 | /* |
| 1028 | * Check SCI as the last resort. |
| 1029 | */ |
| 1030 | if (link->l_isa_irq && !PCI_INTERRUPT_VALID(best_irq)) { |
| 1031 | pos_irq = AcpiGbl_FADT.SciInterrupt; |
| 1032 | pos_weight = pci_link_interrupt_weights[pos_irq]; |
| 1033 | if (pos_weight < best_weight) { |
| 1034 | best_weight = pos_weight; |
| 1035 | best_irq = pos_irq; |
| 1036 | } |
| 1037 | } |
| 1038 | |
| 1039 | if (PCI_INTERRUPT_VALID(best_irq)) { |
| 1040 | if (bootverbose) |
| 1041 | device_printf(dev, "Picked IRQ %u with weight %d\n", |
| 1042 | best_irq, best_weight); |
| 1043 | } else |
| 1044 | device_printf(dev, "Unable to choose an IRQ\n"); |
| 1045 | return (best_irq); |
| 1046 | } |
| 1047 | |
| 1048 | int |
| 1049 | acpi_pci_link_route_interrupt(device_t dev, int index) |
| 1050 | { |
| 1051 | struct link *link; |
| 1052 | |
| 1053 | if (acpi_disabled("pci_link")) |
| 1054 | return (PCI_INVALID_IRQ); |
| 1055 | |
| 1056 | ACPI_SERIAL_BEGIN(pci_link); |
| 1057 | link = acpi_pci_link_lookup(dev, index); |
| 1058 | if (link == NULL) |
| 1059 | panic("%s: apparently invalid index %d", __func__, index); |
| 1060 | |
| 1061 | /* |
| 1062 | * If this link device is already routed to an interrupt, just return |
| 1063 | * the interrupt it is routed to. |
| 1064 | */ |
| 1065 | if (link->l_routed) { |
| 1066 | KASSERT(PCI_INTERRUPT_VALID(link->l_irq), |
| 1067 | ("%s: link is routed but has an invalid IRQ", __func__)); |
| 1068 | ACPI_SERIAL_END(pci_link); |
| 1069 | return (link->l_irq); |
| 1070 | } |
| 1071 | |
| 1072 | /* Choose an IRQ if we need one. */ |
| 1073 | if (!PCI_INTERRUPT_VALID(link->l_irq)) { |
| 1074 | link->l_irq = acpi_pci_link_choose_irq(dev, link); |
| 1075 | |
| 1076 | /* |
| 1077 | * Try to route the interrupt we picked. If it fails, then |
| 1078 | * assume the interrupt is not routed. |
| 1079 | */ |
| 1080 | if (PCI_INTERRUPT_VALID(link->l_irq)) { |
| 1081 | acpi_pci_link_route_irqs(dev); |
| 1082 | if (!link->l_routed) |
| 1083 | link->l_irq = PCI_INVALID_IRQ; |
| 1084 | } |
| 1085 | } |
| 1086 | ACPI_SERIAL_END(pci_link); |
| 1087 | return (link->l_irq); |
| 1088 | } |
| 1089 | |
| 1090 | static device_method_t acpi_pci_link_methods[] = { |
| 1091 | /* Device interface */ |
| 1092 | DEVMETHOD(device_probe, acpi_pci_link_probe), |
| 1093 | DEVMETHOD(device_attach, acpi_pci_link_attach), |
| 1094 | DEVMETHOD(device_resume, acpi_pci_link_resume), |
| 1095 | |
| 1096 | {0, 0} |
| 1097 | }; |
| 1098 | |
| 1099 | static driver_t acpi_pci_link_driver = { |
| 1100 | "pci_link", |
| 1101 | acpi_pci_link_methods, |
| 1102 | sizeof(struct acpi_pci_link_softc), |
| 1103 | }; |
| 1104 | |
| 1105 | static devclass_t pci_link_devclass; |
| 1106 | |
| 1107 | DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, pci_link_devclass, 0, |
| 1108 | 0); |
| 1109 | MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1); |