15db2f26eSSascha Wildner /*
25db2f26eSSascha Wildner * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.kfreebsd.org>
35db2f26eSSascha Wildner * All rights reserved.
45db2f26eSSascha Wildner *
55db2f26eSSascha Wildner * Redistribution and use in source and binary forms, with or without
65db2f26eSSascha Wildner * modification, are permitted provided that the following conditions
75db2f26eSSascha Wildner * are met:
85db2f26eSSascha Wildner * 1. Redistributions of source code must retain the above copyright
95db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer.
105db2f26eSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
115db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer in the
125db2f26eSSascha Wildner * documentation and/or other materials provided with the distribution.
135db2f26eSSascha Wildner *
145db2f26eSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
155db2f26eSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
165db2f26eSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
175db2f26eSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
185db2f26eSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
195db2f26eSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
205db2f26eSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
215db2f26eSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
225db2f26eSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
235db2f26eSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
245db2f26eSSascha Wildner * SUCH DAMAGE.
255db2f26eSSascha Wildner *
2606871c60SSascha Wildner * $FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.65 2013/04/23 00:40:24 svnexp Exp $
275db2f26eSSascha Wildner */
285db2f26eSSascha Wildner
295db2f26eSSascha Wildner #include "opt_acpi.h"
305db2f26eSSascha Wildner #include <sys/param.h>
315db2f26eSSascha Wildner #include <sys/bus.h>
325db2f26eSSascha Wildner #include <sys/kernel.h>
335db2f26eSSascha Wildner #include <sys/limits.h>
345db2f26eSSascha Wildner #include <sys/malloc.h>
355db2f26eSSascha Wildner #include <sys/module.h>
365db2f26eSSascha Wildner
375db2f26eSSascha Wildner #include "acpi.h"
385db2f26eSSascha Wildner #include <dev/acpica/acpivar.h>
395db2f26eSSascha Wildner #include <dev/acpica/acpi_pcibvar.h>
405db2f26eSSascha Wildner #include <dev/acpica/acpi_sci_var.h>
415db2f26eSSascha Wildner
427fdff911SSascha Wildner #include <bus/pci/pci_cfgreg.h>
435db2f26eSSascha Wildner #include <bus/pci/pcireg.h>
445db2f26eSSascha Wildner #include <bus/pci/pcivar.h>
455db2f26eSSascha Wildner #include "pcib_if.h"
465db2f26eSSascha Wildner
475db2f26eSSascha Wildner /* Hooks for the ACPICA debugging infrastructure. */
485db2f26eSSascha Wildner #define _COMPONENT ACPI_BUS
495db2f26eSSascha Wildner ACPI_MODULE_NAME("PCI_LINK")
505db2f26eSSascha Wildner
515db2f26eSSascha Wildner ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
525db2f26eSSascha Wildner
535db2f26eSSascha Wildner #define NUM_ISA_INTERRUPTS 16
545db2f26eSSascha Wildner #define NUM_ACPI_INTERRUPTS 256
555db2f26eSSascha Wildner
565db2f26eSSascha Wildner /*
575db2f26eSSascha Wildner * An ACPI PCI link device may contain multiple links. Each link has its
585db2f26eSSascha Wildner * own ACPI resource. _PRT entries specify which link is being used via
595db2f26eSSascha Wildner * the Source Index.
605db2f26eSSascha Wildner *
615db2f26eSSascha Wildner * XXX: A note about Source Indices and DPFs: Currently we assume that
625db2f26eSSascha Wildner * the DPF start and end tags are not counted towards the index that
635db2f26eSSascha Wildner * Source Index corresponds to. Also, we assume that when DPFs are in use
645db2f26eSSascha Wildner * they various sets overlap in terms of Indices. Here's an example
655db2f26eSSascha Wildner * resource list indicating these assumptions:
665db2f26eSSascha Wildner *
675db2f26eSSascha Wildner * Resource Index
685db2f26eSSascha Wildner * -------- -----
695db2f26eSSascha Wildner * I/O Port 0
705db2f26eSSascha Wildner * Start DPF -
715db2f26eSSascha Wildner * IRQ 1
725db2f26eSSascha Wildner * MemIO 2
735db2f26eSSascha Wildner * Start DPF -
745db2f26eSSascha Wildner * IRQ 1
755db2f26eSSascha Wildner * MemIO 2
765db2f26eSSascha Wildner * End DPF -
775db2f26eSSascha Wildner * DMA Channel 3
785db2f26eSSascha Wildner *
795db2f26eSSascha Wildner * The XXX is because I'm not sure if this is a valid assumption to make.
805db2f26eSSascha Wildner */
815db2f26eSSascha Wildner
825db2f26eSSascha Wildner /* States during DPF processing. */
835db2f26eSSascha Wildner #define DPF_OUTSIDE 0
845db2f26eSSascha Wildner #define DPF_FIRST 1
855db2f26eSSascha Wildner #define DPF_IGNORE 2
865db2f26eSSascha Wildner
875db2f26eSSascha Wildner struct link;
885db2f26eSSascha Wildner
895db2f26eSSascha Wildner struct acpi_pci_link_softc {
905db2f26eSSascha Wildner int pl_num_links;
915db2f26eSSascha Wildner int pl_crs_bad;
925db2f26eSSascha Wildner struct link *pl_links;
935db2f26eSSascha Wildner device_t pl_dev;
945db2f26eSSascha Wildner };
955db2f26eSSascha Wildner
965db2f26eSSascha Wildner struct link {
975db2f26eSSascha Wildner struct acpi_pci_link_softc *l_sc;
985db2f26eSSascha Wildner uint8_t l_bios_irq;
995db2f26eSSascha Wildner uint8_t l_irq;
1005db2f26eSSascha Wildner uint8_t l_initial_irq;
10106871c60SSascha Wildner UINT32 l_crs_type;
1025db2f26eSSascha Wildner int l_res_index;
1035db2f26eSSascha Wildner int l_num_irqs;
1045db2f26eSSascha Wildner int *l_irqs;
1055db2f26eSSascha Wildner int l_references;
1065db2f26eSSascha Wildner int l_routed:1;
1075db2f26eSSascha Wildner int l_isa_irq:1;
1085db2f26eSSascha Wildner ACPI_RESOURCE l_prs_template;
1095db2f26eSSascha Wildner };
1105db2f26eSSascha Wildner
1115db2f26eSSascha Wildner struct link_count_request {
1125db2f26eSSascha Wildner int in_dpf;
1135db2f26eSSascha Wildner int count;
1145db2f26eSSascha Wildner };
1155db2f26eSSascha Wildner
1165db2f26eSSascha Wildner struct link_res_request {
1175db2f26eSSascha Wildner struct acpi_pci_link_softc *sc;
1185db2f26eSSascha Wildner int in_dpf;
1195db2f26eSSascha Wildner int res_index;
1205db2f26eSSascha Wildner int link_index;
1215db2f26eSSascha Wildner };
1225db2f26eSSascha Wildner
12306871c60SSascha Wildner static MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures");
1245db2f26eSSascha Wildner
1255db2f26eSSascha Wildner static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
1265db2f26eSSascha Wildner static int pci_link_bios_isa_irqs;
1275db2f26eSSascha Wildner
1285db2f26eSSascha Wildner static char *pci_link_ids[] = { "PNP0C0F", NULL };
1295db2f26eSSascha Wildner
1305db2f26eSSascha Wildner /*
1315db2f26eSSascha Wildner * Fetch the short name associated with an ACPI handle and save it in the
1325db2f26eSSascha Wildner * passed in buffer.
1335db2f26eSSascha Wildner */
1345db2f26eSSascha Wildner static ACPI_STATUS
acpi_short_name(ACPI_HANDLE handle,char * buffer,size_t buflen)1355db2f26eSSascha Wildner acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
1365db2f26eSSascha Wildner {
1375db2f26eSSascha Wildner ACPI_BUFFER buf;
1385db2f26eSSascha Wildner
1395db2f26eSSascha Wildner buf.Length = buflen;
1405db2f26eSSascha Wildner buf.Pointer = buffer;
1415db2f26eSSascha Wildner return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
1425db2f26eSSascha Wildner }
1435db2f26eSSascha Wildner
1445db2f26eSSascha Wildner static int
acpi_pci_link_probe(device_t dev)1455db2f26eSSascha Wildner acpi_pci_link_probe(device_t dev)
1465db2f26eSSascha Wildner {
1475db2f26eSSascha Wildner char descr[28], name[12];
1485db2f26eSSascha Wildner
1495db2f26eSSascha Wildner /*
1505db2f26eSSascha Wildner * We explicitly do not check _STA since not all systems set it to
1515db2f26eSSascha Wildner * sensible values.
1525db2f26eSSascha Wildner */
1535db2f26eSSascha Wildner if (acpi_disabled("pci_link") ||
1545db2f26eSSascha Wildner ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids) == NULL)
1555db2f26eSSascha Wildner return (ENXIO);
1565db2f26eSSascha Wildner
1575db2f26eSSascha Wildner if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
1585db2f26eSSascha Wildner sizeof(name)))) {
1595db2f26eSSascha Wildner ksnprintf(descr, sizeof(descr), "ACPI PCI Link %s", name);
1605db2f26eSSascha Wildner device_set_desc_copy(dev, descr);
1615db2f26eSSascha Wildner } else
1625db2f26eSSascha Wildner device_set_desc(dev, "ACPI PCI Link");
1635db2f26eSSascha Wildner device_quiet(dev);
1645db2f26eSSascha Wildner return (0);
1655db2f26eSSascha Wildner }
1665db2f26eSSascha Wildner
1675db2f26eSSascha Wildner static ACPI_STATUS
acpi_count_irq_resources(ACPI_RESOURCE * res,void * context)1685db2f26eSSascha Wildner acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
1695db2f26eSSascha Wildner {
1705db2f26eSSascha Wildner struct link_count_request *req;
1715db2f26eSSascha Wildner
1725db2f26eSSascha Wildner req = (struct link_count_request *)context;
1735db2f26eSSascha Wildner switch (res->Type) {
1745db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_START_DEPENDENT:
1755db2f26eSSascha Wildner switch (req->in_dpf) {
1765db2f26eSSascha Wildner case DPF_OUTSIDE:
1775db2f26eSSascha Wildner /* We've started the first DPF. */
1785db2f26eSSascha Wildner req->in_dpf = DPF_FIRST;
1795db2f26eSSascha Wildner break;
1805db2f26eSSascha Wildner case DPF_FIRST:
1815db2f26eSSascha Wildner /* We've started the second DPF. */
1825db2f26eSSascha Wildner req->in_dpf = DPF_IGNORE;
1835db2f26eSSascha Wildner break;
1845db2f26eSSascha Wildner }
1855db2f26eSSascha Wildner break;
1865db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_END_DEPENDENT:
1875db2f26eSSascha Wildner /* We are finished with DPF parsing. */
1885db2f26eSSascha Wildner KASSERT(req->in_dpf != DPF_OUTSIDE,
1895db2f26eSSascha Wildner ("%s: end dpf when not parsing a dpf", __func__));
1905db2f26eSSascha Wildner req->in_dpf = DPF_OUTSIDE;
1915db2f26eSSascha Wildner break;
1925db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_IRQ:
1935db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
1945db2f26eSSascha Wildner /*
1955db2f26eSSascha Wildner * Don't count resources if we are in a DPF set that we are
1965db2f26eSSascha Wildner * ignoring.
1975db2f26eSSascha Wildner */
1985db2f26eSSascha Wildner if (req->in_dpf != DPF_IGNORE)
1995db2f26eSSascha Wildner req->count++;
2005db2f26eSSascha Wildner }
2015db2f26eSSascha Wildner return (AE_OK);
2025db2f26eSSascha Wildner }
2035db2f26eSSascha Wildner
2045db2f26eSSascha Wildner static ACPI_STATUS
link_add_crs(ACPI_RESOURCE * res,void * context)2055db2f26eSSascha Wildner link_add_crs(ACPI_RESOURCE *res, void *context)
2065db2f26eSSascha Wildner {
2075db2f26eSSascha Wildner struct link_res_request *req;
2085db2f26eSSascha Wildner struct link *link;
2095db2f26eSSascha Wildner
2105db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
2115db2f26eSSascha Wildner req = (struct link_res_request *)context;
2125db2f26eSSascha Wildner switch (res->Type) {
2135db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_START_DEPENDENT:
2145db2f26eSSascha Wildner switch (req->in_dpf) {
2155db2f26eSSascha Wildner case DPF_OUTSIDE:
2165db2f26eSSascha Wildner /* We've started the first DPF. */
2175db2f26eSSascha Wildner req->in_dpf = DPF_FIRST;
2185db2f26eSSascha Wildner break;
2195db2f26eSSascha Wildner case DPF_FIRST:
2205db2f26eSSascha Wildner /* We've started the second DPF. */
2215db2f26eSSascha Wildner panic(
2225db2f26eSSascha Wildner "%s: Multiple dependent functions within a current resource",
2235db2f26eSSascha Wildner __func__);
2245db2f26eSSascha Wildner break;
2255db2f26eSSascha Wildner }
2265db2f26eSSascha Wildner break;
2275db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_END_DEPENDENT:
2285db2f26eSSascha Wildner /* We are finished with DPF parsing. */
2295db2f26eSSascha Wildner KASSERT(req->in_dpf != DPF_OUTSIDE,
2305db2f26eSSascha Wildner ("%s: end dpf when not parsing a dpf", __func__));
2315db2f26eSSascha Wildner req->in_dpf = DPF_OUTSIDE;
2325db2f26eSSascha Wildner break;
2335db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_IRQ:
2345db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
2355db2f26eSSascha Wildner KASSERT(req->link_index < req->sc->pl_num_links,
2365db2f26eSSascha Wildner ("%s: array boundary violation", __func__));
2375db2f26eSSascha Wildner link = &req->sc->pl_links[req->link_index];
2385db2f26eSSascha Wildner link->l_res_index = req->res_index;
23906871c60SSascha Wildner link->l_crs_type = res->Type;
2405db2f26eSSascha Wildner req->link_index++;
2415db2f26eSSascha Wildner req->res_index++;
2425db2f26eSSascha Wildner
2435db2f26eSSascha Wildner /*
2445db2f26eSSascha Wildner * Only use the current value if there's one IRQ. Some
2455db2f26eSSascha Wildner * systems return multiple IRQs (which is nonsense for _CRS)
2465db2f26eSSascha Wildner * when the link hasn't been programmed.
2475db2f26eSSascha Wildner */
2485db2f26eSSascha Wildner if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
2495db2f26eSSascha Wildner if (res->Data.Irq.InterruptCount == 1)
2505db2f26eSSascha Wildner link->l_irq = res->Data.Irq.Interrupts[0];
2515db2f26eSSascha Wildner } else if (res->Data.ExtendedIrq.InterruptCount == 1)
2525db2f26eSSascha Wildner link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
2535db2f26eSSascha Wildner
2545db2f26eSSascha Wildner /*
2555db2f26eSSascha Wildner * An IRQ of zero means that the link isn't routed.
2565db2f26eSSascha Wildner */
2575db2f26eSSascha Wildner if (link->l_irq == 0)
2585db2f26eSSascha Wildner link->l_irq = PCI_INVALID_IRQ;
2595db2f26eSSascha Wildner break;
2605db2f26eSSascha Wildner default:
2615db2f26eSSascha Wildner req->res_index++;
2625db2f26eSSascha Wildner }
2635db2f26eSSascha Wildner return (AE_OK);
2645db2f26eSSascha Wildner }
2655db2f26eSSascha Wildner
2665db2f26eSSascha Wildner /*
2675db2f26eSSascha Wildner * Populate the set of possible IRQs for each device.
2685db2f26eSSascha Wildner */
2695db2f26eSSascha Wildner static ACPI_STATUS
link_add_prs(ACPI_RESOURCE * res,void * context)2705db2f26eSSascha Wildner link_add_prs(ACPI_RESOURCE *res, void *context)
2715db2f26eSSascha Wildner {
27206871c60SSascha Wildner ACPI_RESOURCE *tmp;
2735db2f26eSSascha Wildner struct link_res_request *req;
2745db2f26eSSascha Wildner struct link *link;
2755db2f26eSSascha Wildner UINT8 *irqs = NULL;
2765db2f26eSSascha Wildner UINT32 *ext_irqs = NULL;
2775db2f26eSSascha Wildner int i, is_ext_irq = 1;
2785db2f26eSSascha Wildner
2795db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
2805db2f26eSSascha Wildner req = (struct link_res_request *)context;
2815db2f26eSSascha Wildner switch (res->Type) {
2825db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_START_DEPENDENT:
2835db2f26eSSascha Wildner switch (req->in_dpf) {
2845db2f26eSSascha Wildner case DPF_OUTSIDE:
2855db2f26eSSascha Wildner /* We've started the first DPF. */
2865db2f26eSSascha Wildner req->in_dpf = DPF_FIRST;
2875db2f26eSSascha Wildner break;
2885db2f26eSSascha Wildner case DPF_FIRST:
2895db2f26eSSascha Wildner /* We've started the second DPF. */
2905db2f26eSSascha Wildner req->in_dpf = DPF_IGNORE;
2915db2f26eSSascha Wildner break;
2925db2f26eSSascha Wildner }
2935db2f26eSSascha Wildner break;
2945db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_END_DEPENDENT:
2955db2f26eSSascha Wildner /* We are finished with DPF parsing. */
2965db2f26eSSascha Wildner KASSERT(req->in_dpf != DPF_OUTSIDE,
2975db2f26eSSascha Wildner ("%s: end dpf when not parsing a dpf", __func__));
2985db2f26eSSascha Wildner req->in_dpf = DPF_OUTSIDE;
2995db2f26eSSascha Wildner break;
3005db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_IRQ:
3015db2f26eSSascha Wildner is_ext_irq = 0;
3025db2f26eSSascha Wildner /* fall through */
3035db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
3045db2f26eSSascha Wildner /*
3055db2f26eSSascha Wildner * Don't parse resources if we are in a DPF set that we are
3065db2f26eSSascha Wildner * ignoring.
3075db2f26eSSascha Wildner */
3085db2f26eSSascha Wildner if (req->in_dpf == DPF_IGNORE)
3095db2f26eSSascha Wildner break;
3105db2f26eSSascha Wildner
3115db2f26eSSascha Wildner KASSERT(req->link_index < req->sc->pl_num_links,
3125db2f26eSSascha Wildner ("%s: array boundary violation", __func__));
3135db2f26eSSascha Wildner link = &req->sc->pl_links[req->link_index];
3145db2f26eSSascha Wildner if (link->l_res_index == -1) {
3155db2f26eSSascha Wildner KASSERT(req->sc->pl_crs_bad,
3165db2f26eSSascha Wildner ("res_index should be set"));
3175db2f26eSSascha Wildner link->l_res_index = req->res_index;
3185db2f26eSSascha Wildner }
3195db2f26eSSascha Wildner req->link_index++;
3205db2f26eSSascha Wildner req->res_index++;
3215db2f26eSSascha Wildner
3225db2f26eSSascha Wildner /*
3235db2f26eSSascha Wildner * Stash a copy of the resource for later use when doing
3245db2f26eSSascha Wildner * _SRS.
3255db2f26eSSascha Wildner */
32606871c60SSascha Wildner tmp = &link->l_prs_template;
3275db2f26eSSascha Wildner if (is_ext_irq) {
32806871c60SSascha Wildner bcopy(res, tmp, ACPI_RS_SIZE(tmp->Data.ExtendedIrq));
32906871c60SSascha Wildner
33006871c60SSascha Wildner /*
33106871c60SSascha Wildner * XXX acpi_AppendBufferResource() cannot handle
33206871c60SSascha Wildner * optional data.
33306871c60SSascha Wildner */
33406871c60SSascha Wildner bzero(&tmp->Data.ExtendedIrq.ResourceSource,
33506871c60SSascha Wildner sizeof(tmp->Data.ExtendedIrq.ResourceSource));
33606871c60SSascha Wildner tmp->Length = ACPI_RS_SIZE(tmp->Data.ExtendedIrq);
33706871c60SSascha Wildner
3385db2f26eSSascha Wildner link->l_num_irqs =
3395db2f26eSSascha Wildner res->Data.ExtendedIrq.InterruptCount;
3405db2f26eSSascha Wildner ext_irqs = res->Data.ExtendedIrq.Interrupts;
3415db2f26eSSascha Wildner } else {
34206871c60SSascha Wildner bcopy(res, tmp, ACPI_RS_SIZE(tmp->Data.Irq));
3435db2f26eSSascha Wildner link->l_num_irqs = res->Data.Irq.InterruptCount;
3445db2f26eSSascha Wildner irqs = res->Data.Irq.Interrupts;
3455db2f26eSSascha Wildner }
3465db2f26eSSascha Wildner if (link->l_num_irqs == 0)
3475db2f26eSSascha Wildner break;
3485db2f26eSSascha Wildner
3495db2f26eSSascha Wildner /*
3505db2f26eSSascha Wildner * Save a list of the valid IRQs. Also, if all of the
3515db2f26eSSascha Wildner * valid IRQs are ISA IRQs, then mark this link as
3525db2f26eSSascha Wildner * routed via an ISA interrupt.
3535db2f26eSSascha Wildner */
3545db2f26eSSascha Wildner link->l_isa_irq = TRUE;
3555db2f26eSSascha Wildner
3565db2f26eSSascha Wildner link->l_irqs = kmalloc(sizeof(int) * link->l_num_irqs,
3575db2f26eSSascha Wildner M_PCI_LINK, M_WAITOK | M_ZERO);
3585db2f26eSSascha Wildner for (i = 0; i < link->l_num_irqs; i++) {
3595db2f26eSSascha Wildner if (is_ext_irq) {
3605db2f26eSSascha Wildner link->l_irqs[i] = ext_irqs[i];
3615db2f26eSSascha Wildner if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
3625db2f26eSSascha Wildner link->l_isa_irq = FALSE;
3635db2f26eSSascha Wildner } else {
3645db2f26eSSascha Wildner link->l_irqs[i] = irqs[i];
3655db2f26eSSascha Wildner if (irqs[i] >= NUM_ISA_INTERRUPTS)
3665db2f26eSSascha Wildner link->l_isa_irq = FALSE;
3675db2f26eSSascha Wildner }
3685db2f26eSSascha Wildner }
36906871c60SSascha Wildner
37006871c60SSascha Wildner /*
37106871c60SSascha Wildner * If this is not an ISA IRQ but _CRS used a non-extended
37206871c60SSascha Wildner * IRQ descriptor, don't use _CRS as a template for _SRS.
37306871c60SSascha Wildner */
37406871c60SSascha Wildner if (!req->sc->pl_crs_bad && !link->l_isa_irq &&
37506871c60SSascha Wildner link->l_crs_type == ACPI_RESOURCE_TYPE_IRQ)
37606871c60SSascha Wildner req->sc->pl_crs_bad = TRUE;
3775db2f26eSSascha Wildner break;
3785db2f26eSSascha Wildner default:
3795db2f26eSSascha Wildner if (req->in_dpf == DPF_IGNORE)
3805db2f26eSSascha Wildner break;
3815db2f26eSSascha Wildner if (req->sc->pl_crs_bad)
3825db2f26eSSascha Wildner device_printf(req->sc->pl_dev,
3835db2f26eSSascha Wildner "Warning: possible resource %d will be lost during _SRS\n",
3845db2f26eSSascha Wildner req->res_index);
3855db2f26eSSascha Wildner req->res_index++;
3865db2f26eSSascha Wildner }
3875db2f26eSSascha Wildner return (AE_OK);
3885db2f26eSSascha Wildner }
3895db2f26eSSascha Wildner
3905db2f26eSSascha Wildner static int
link_valid_irq(struct link * link,int irq)3915db2f26eSSascha Wildner link_valid_irq(struct link *link, int irq)
3925db2f26eSSascha Wildner {
3935db2f26eSSascha Wildner int i;
3945db2f26eSSascha Wildner
3955db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
3965db2f26eSSascha Wildner
3975db2f26eSSascha Wildner /* Invalid interrupts are never valid. */
3985db2f26eSSascha Wildner if (!PCI_INTERRUPT_VALID(irq))
3995db2f26eSSascha Wildner return (FALSE);
4005db2f26eSSascha Wildner
4015db2f26eSSascha Wildner /* Any interrupt in the list of possible interrupts is valid. */
4025db2f26eSSascha Wildner for (i = 0; i < link->l_num_irqs; i++)
4035db2f26eSSascha Wildner if (link->l_irqs[i] == irq)
4045db2f26eSSascha Wildner return (TRUE);
4055db2f26eSSascha Wildner
4065db2f26eSSascha Wildner /*
4075db2f26eSSascha Wildner * For links routed via an ISA interrupt, if the SCI is routed via
4085db2f26eSSascha Wildner * an ISA interrupt, the SCI is always treated as a valid IRQ.
4095db2f26eSSascha Wildner */
4105db2f26eSSascha Wildner if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
4115db2f26eSSascha Wildner irq < NUM_ISA_INTERRUPTS)
4125db2f26eSSascha Wildner return (TRUE);
4135db2f26eSSascha Wildner
4145db2f26eSSascha Wildner /* If the interrupt wasn't found in the list it is not valid. */
4155db2f26eSSascha Wildner return (FALSE);
4165db2f26eSSascha Wildner }
4175db2f26eSSascha Wildner
4185db2f26eSSascha Wildner static void
acpi_pci_link_dump(struct acpi_pci_link_softc * sc,int header,const char * tag)4195db2f26eSSascha Wildner acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag)
4205db2f26eSSascha Wildner {
4215db2f26eSSascha Wildner struct link *link;
4225db2f26eSSascha Wildner char buf[16];
4235db2f26eSSascha Wildner int i, j;
4245db2f26eSSascha Wildner
4255db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
4265db2f26eSSascha Wildner if (header) {
4275db2f26eSSascha Wildner ksnprintf(buf, sizeof(buf), "%s:",
4285db2f26eSSascha Wildner device_get_nameunit(sc->pl_dev));
4295db2f26eSSascha Wildner kprintf("%-16.16s Index IRQ Rtd Ref IRQs\n", buf);
4305db2f26eSSascha Wildner }
4315db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++) {
4325db2f26eSSascha Wildner link = &sc->pl_links[i];
4335db2f26eSSascha Wildner kprintf(" %-14.14s %5d %3d %c %3d ", i == 0 ? tag : "", i,
4345db2f26eSSascha Wildner link->l_irq, link->l_routed ? 'Y' : 'N',
4355db2f26eSSascha Wildner link->l_references);
4365db2f26eSSascha Wildner if (link->l_num_irqs == 0)
4375db2f26eSSascha Wildner kprintf(" none");
4385db2f26eSSascha Wildner else for (j = 0; j < link->l_num_irqs; j++)
4395db2f26eSSascha Wildner kprintf(" %d", link->l_irqs[j]);
4405db2f26eSSascha Wildner kprintf("\n");
4415db2f26eSSascha Wildner }
4425db2f26eSSascha Wildner }
4435db2f26eSSascha Wildner
4445db2f26eSSascha Wildner static int
acpi_pci_link_attach(device_t dev)4455db2f26eSSascha Wildner acpi_pci_link_attach(device_t dev)
4465db2f26eSSascha Wildner {
4475db2f26eSSascha Wildner struct acpi_pci_link_softc *sc;
4485db2f26eSSascha Wildner struct link_count_request creq;
4495db2f26eSSascha Wildner struct link_res_request rreq;
4505db2f26eSSascha Wildner ACPI_STATUS status;
4515db2f26eSSascha Wildner int i;
4525db2f26eSSascha Wildner
4535db2f26eSSascha Wildner sc = device_get_softc(dev);
4545db2f26eSSascha Wildner sc->pl_dev = dev;
4555db2f26eSSascha Wildner ACPI_SERIAL_INIT(pci_link);
4565db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(pci_link);
4575db2f26eSSascha Wildner
4585db2f26eSSascha Wildner /*
4595db2f26eSSascha Wildner * Count the number of current resources so we know how big of
4605db2f26eSSascha Wildner * a link array to allocate. On some systems, _CRS is broken,
4615db2f26eSSascha Wildner * so for those systems try to derive the count from _PRS instead.
4625db2f26eSSascha Wildner */
4635db2f26eSSascha Wildner creq.in_dpf = DPF_OUTSIDE;
4645db2f26eSSascha Wildner creq.count = 0;
4655db2f26eSSascha Wildner status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
4665db2f26eSSascha Wildner acpi_count_irq_resources, &creq);
4675db2f26eSSascha Wildner sc->pl_crs_bad = ACPI_FAILURE(status);
4685db2f26eSSascha Wildner if (sc->pl_crs_bad) {
4695db2f26eSSascha Wildner creq.in_dpf = DPF_OUTSIDE;
4705db2f26eSSascha Wildner creq.count = 0;
4715db2f26eSSascha Wildner status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
4725db2f26eSSascha Wildner acpi_count_irq_resources, &creq);
4735db2f26eSSascha Wildner if (ACPI_FAILURE(status)) {
4745db2f26eSSascha Wildner device_printf(dev,
4755db2f26eSSascha Wildner "Unable to parse _CRS or _PRS: %s\n",
4765db2f26eSSascha Wildner AcpiFormatException(status));
4775db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
4785db2f26eSSascha Wildner return (ENXIO);
4795db2f26eSSascha Wildner }
4805db2f26eSSascha Wildner }
4815db2f26eSSascha Wildner sc->pl_num_links = creq.count;
4825db2f26eSSascha Wildner if (creq.count == 0) {
4835db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
4845db2f26eSSascha Wildner return (0);
4855db2f26eSSascha Wildner }
4865db2f26eSSascha Wildner sc->pl_links = kmalloc(sizeof(struct link) * sc->pl_num_links,
4875db2f26eSSascha Wildner M_PCI_LINK, M_WAITOK | M_ZERO);
4885db2f26eSSascha Wildner
4895db2f26eSSascha Wildner /* Initialize the child links. */
4905db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++) {
4915db2f26eSSascha Wildner sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
4925db2f26eSSascha Wildner sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
4935db2f26eSSascha Wildner sc->pl_links[i].l_sc = sc;
4945db2f26eSSascha Wildner sc->pl_links[i].l_isa_irq = FALSE;
4955db2f26eSSascha Wildner sc->pl_links[i].l_res_index = -1;
4965db2f26eSSascha Wildner }
4975db2f26eSSascha Wildner
4985db2f26eSSascha Wildner /* Try to read the current settings from _CRS if it is valid. */
4995db2f26eSSascha Wildner if (!sc->pl_crs_bad) {
5005db2f26eSSascha Wildner rreq.in_dpf = DPF_OUTSIDE;
5015db2f26eSSascha Wildner rreq.link_index = 0;
5025db2f26eSSascha Wildner rreq.res_index = 0;
5035db2f26eSSascha Wildner rreq.sc = sc;
5045db2f26eSSascha Wildner status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
5055db2f26eSSascha Wildner link_add_crs, &rreq);
5065db2f26eSSascha Wildner if (ACPI_FAILURE(status)) {
5075db2f26eSSascha Wildner device_printf(dev, "Unable to parse _CRS: %s\n",
5085db2f26eSSascha Wildner AcpiFormatException(status));
5095db2f26eSSascha Wildner goto fail;
5105db2f26eSSascha Wildner }
5115db2f26eSSascha Wildner }
5125db2f26eSSascha Wildner
5135db2f26eSSascha Wildner /*
5145db2f26eSSascha Wildner * Try to read the possible settings from _PRS. Note that if the
5155db2f26eSSascha Wildner * _CRS is toast, we depend on having a working _PRS. However, if
5165db2f26eSSascha Wildner * _CRS works, then it is ok for _PRS to be missing.
5175db2f26eSSascha Wildner */
5185db2f26eSSascha Wildner rreq.in_dpf = DPF_OUTSIDE;
5195db2f26eSSascha Wildner rreq.link_index = 0;
5205db2f26eSSascha Wildner rreq.res_index = 0;
5215db2f26eSSascha Wildner rreq.sc = sc;
5225db2f26eSSascha Wildner status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
5235db2f26eSSascha Wildner link_add_prs, &rreq);
5245db2f26eSSascha Wildner if (ACPI_FAILURE(status) &&
5255db2f26eSSascha Wildner (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
5265db2f26eSSascha Wildner device_printf(dev, "Unable to parse _PRS: %s\n",
5275db2f26eSSascha Wildner AcpiFormatException(status));
5285db2f26eSSascha Wildner goto fail;
5295db2f26eSSascha Wildner }
5305db2f26eSSascha Wildner if (bootverbose)
5315db2f26eSSascha Wildner acpi_pci_link_dump(sc, 1, "Initial Probe");
5325db2f26eSSascha Wildner
5335db2f26eSSascha Wildner /* Verify initial IRQs if we have _PRS. */
5345db2f26eSSascha Wildner if (status != AE_NOT_FOUND)
5355db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
5365db2f26eSSascha Wildner if (!link_valid_irq(&sc->pl_links[i],
5375db2f26eSSascha Wildner sc->pl_links[i].l_irq))
5385db2f26eSSascha Wildner sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
5395db2f26eSSascha Wildner if (bootverbose)
5405db2f26eSSascha Wildner acpi_pci_link_dump(sc, 0, "Validation");
5415db2f26eSSascha Wildner
5425db2f26eSSascha Wildner /* Save initial IRQs. */
5435db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
5445db2f26eSSascha Wildner sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
5455db2f26eSSascha Wildner
5465db2f26eSSascha Wildner /*
5475db2f26eSSascha Wildner * Try to disable this link. If successful, set the current IRQ to
5485db2f26eSSascha Wildner * zero and flags to indicate this link is not routed. If we can't
5495db2f26eSSascha Wildner * run _DIS (i.e., the method doesn't exist), assume the initial
5505db2f26eSSascha Wildner * IRQ was routed by the BIOS.
5515db2f26eSSascha Wildner */
5525db2f26eSSascha Wildner if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
5535db2f26eSSascha Wildner NULL)))
5545db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
5555db2f26eSSascha Wildner sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
5565db2f26eSSascha Wildner else
5575db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
5585db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
5595db2f26eSSascha Wildner sc->pl_links[i].l_routed = TRUE;
5605db2f26eSSascha Wildner if (bootverbose)
5615db2f26eSSascha Wildner acpi_pci_link_dump(sc, 0, "After Disable");
5625db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
5635db2f26eSSascha Wildner return (0);
5645db2f26eSSascha Wildner fail:
5655db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
5665db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
5675db2f26eSSascha Wildner if (sc->pl_links[i].l_irqs != NULL)
5685db2f26eSSascha Wildner kfree(sc->pl_links[i].l_irqs, M_PCI_LINK);
5695db2f26eSSascha Wildner kfree(sc->pl_links, M_PCI_LINK);
5705db2f26eSSascha Wildner return (ENXIO);
5715db2f26eSSascha Wildner }
5725db2f26eSSascha Wildner
5735db2f26eSSascha Wildner /* XXX: Note that this is identical to pci_pir_search_irq(). */
5745db2f26eSSascha Wildner static uint8_t
acpi_pci_link_search_irq(int bus,int device,int pin)5755db2f26eSSascha Wildner acpi_pci_link_search_irq(int bus, int device, int pin)
5765db2f26eSSascha Wildner {
5775db2f26eSSascha Wildner uint32_t value;
5785db2f26eSSascha Wildner uint8_t func, maxfunc;
5795db2f26eSSascha Wildner
5805db2f26eSSascha Wildner /* See if we have a valid device at function 0. */
5815db2f26eSSascha Wildner value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
5825db2f26eSSascha Wildner if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
5835db2f26eSSascha Wildner return (PCI_INVALID_IRQ);
5845db2f26eSSascha Wildner if (value & PCIM_MFDEV)
5855db2f26eSSascha Wildner maxfunc = PCI_FUNCMAX;
5865db2f26eSSascha Wildner else
5875db2f26eSSascha Wildner maxfunc = 0;
5885db2f26eSSascha Wildner
5895db2f26eSSascha Wildner /* Scan all possible functions at this device. */
5905db2f26eSSascha Wildner for (func = 0; func <= maxfunc; func++) {
5915db2f26eSSascha Wildner value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
5925db2f26eSSascha Wildner if (value == 0xffffffff)
5935db2f26eSSascha Wildner continue;
5945db2f26eSSascha Wildner value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
5955db2f26eSSascha Wildner
5965db2f26eSSascha Wildner /*
5975db2f26eSSascha Wildner * See if it uses the pin in question. Note that the passed
5985db2f26eSSascha Wildner * in pin uses 0 for A, .. 3 for D whereas the intpin
5995db2f26eSSascha Wildner * register uses 0 for no interrupt, 1 for A, .. 4 for D.
6005db2f26eSSascha Wildner */
6015db2f26eSSascha Wildner if (value != pin + 1)
6025db2f26eSSascha Wildner continue;
6035db2f26eSSascha Wildner value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
6045db2f26eSSascha Wildner if (bootverbose)
6055db2f26eSSascha Wildner kprintf(
6065db2f26eSSascha Wildner "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
6075db2f26eSSascha Wildner bus, device, pin + 'A', func, value);
6085db2f26eSSascha Wildner if (value != PCI_INVALID_IRQ)
6095db2f26eSSascha Wildner return (value);
6105db2f26eSSascha Wildner }
6115db2f26eSSascha Wildner return (PCI_INVALID_IRQ);
6125db2f26eSSascha Wildner }
6135db2f26eSSascha Wildner
6145db2f26eSSascha Wildner /*
6155db2f26eSSascha Wildner * Find the link structure that corresponds to the resource index passed in
6165db2f26eSSascha Wildner * via 'source_index'.
6175db2f26eSSascha Wildner */
6185db2f26eSSascha Wildner static struct link *
acpi_pci_link_lookup(device_t dev,int source_index)6195db2f26eSSascha Wildner acpi_pci_link_lookup(device_t dev, int source_index)
6205db2f26eSSascha Wildner {
6215db2f26eSSascha Wildner struct acpi_pci_link_softc *sc;
6225db2f26eSSascha Wildner int i;
6235db2f26eSSascha Wildner
6245db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
6255db2f26eSSascha Wildner sc = device_get_softc(dev);
6265db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
6275db2f26eSSascha Wildner if (sc->pl_links[i].l_res_index == source_index)
6285db2f26eSSascha Wildner return (&sc->pl_links[i]);
6295db2f26eSSascha Wildner return (NULL);
6305db2f26eSSascha Wildner }
6315db2f26eSSascha Wildner
6325db2f26eSSascha Wildner void
acpi_pci_link_add_reference(device_t dev,int index,device_t pcib,int slot,int pin)6335db2f26eSSascha Wildner acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
6345db2f26eSSascha Wildner int pin)
6355db2f26eSSascha Wildner {
6365db2f26eSSascha Wildner struct link *link;
6375db2f26eSSascha Wildner uint8_t bios_irq;
6385db2f26eSSascha Wildner uintptr_t bus;
6395db2f26eSSascha Wildner
6405db2f26eSSascha Wildner /*
6415db2f26eSSascha Wildner * Look up the PCI bus for the specified PCI bridge device. Note
6425db2f26eSSascha Wildner * that the PCI bridge device might not have any children yet.
6435db2f26eSSascha Wildner * However, looking up its bus number doesn't require a valid child
6445db2f26eSSascha Wildner * device, so we just pass NULL.
6455db2f26eSSascha Wildner */
6465db2f26eSSascha Wildner if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
6475db2f26eSSascha Wildner device_printf(pcib, "Unable to read PCI bus number");
6485db2f26eSSascha Wildner panic("PCI bridge without a bus number");
6495db2f26eSSascha Wildner }
6505db2f26eSSascha Wildner
6515db2f26eSSascha Wildner /* Bump the reference count. */
6525db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(pci_link);
6535db2f26eSSascha Wildner link = acpi_pci_link_lookup(dev, index);
6545db2f26eSSascha Wildner if (link == NULL) {
6555db2f26eSSascha Wildner device_printf(dev, "apparently invalid index %d\n", index);
6565db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
6575db2f26eSSascha Wildner return;
6585db2f26eSSascha Wildner }
6595db2f26eSSascha Wildner link->l_references++;
6605db2f26eSSascha Wildner if (link->l_routed)
6615db2f26eSSascha Wildner pci_link_interrupt_weights[link->l_irq]++;
6625db2f26eSSascha Wildner
6635db2f26eSSascha Wildner /*
6645db2f26eSSascha Wildner * The BIOS only routes interrupts via ISA IRQs using the ATPICs
6655db2f26eSSascha Wildner * (8259As). Thus, if this link is routed via an ISA IRQ, go
6665db2f26eSSascha Wildner * look to see if the BIOS routed an IRQ for this link at the
6675db2f26eSSascha Wildner * indicated (bus, slot, pin). If so, we prefer that IRQ for
6685db2f26eSSascha Wildner * this link and add that IRQ to our list of known-good IRQs.
6695db2f26eSSascha Wildner * This provides a good work-around for link devices whose _CRS
6705db2f26eSSascha Wildner * method is either broken or bogus. We only use the value
6715db2f26eSSascha Wildner * returned by _CRS if we can't find a valid IRQ via this method
6725db2f26eSSascha Wildner * in fact.
6735db2f26eSSascha Wildner *
6745db2f26eSSascha Wildner * If this link is not routed via an ISA IRQ (because we are using
6755db2f26eSSascha Wildner * APIC for example), then don't bother looking up the BIOS IRQ
6765db2f26eSSascha Wildner * as if we find one it won't be valid anyway.
6775db2f26eSSascha Wildner */
6785db2f26eSSascha Wildner if (!link->l_isa_irq) {
6795db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
6805db2f26eSSascha Wildner return;
6815db2f26eSSascha Wildner }
6825db2f26eSSascha Wildner
6835db2f26eSSascha Wildner /* Try to find a BIOS IRQ setting from any matching devices. */
6845db2f26eSSascha Wildner bios_irq = acpi_pci_link_search_irq(bus, slot, pin);
6855db2f26eSSascha Wildner if (!PCI_INTERRUPT_VALID(bios_irq)) {
6865db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
6875db2f26eSSascha Wildner return;
6885db2f26eSSascha Wildner }
6895db2f26eSSascha Wildner
6905db2f26eSSascha Wildner /* Validate the BIOS IRQ. */
6915db2f26eSSascha Wildner if (!link_valid_irq(link, bios_irq)) {
6925db2f26eSSascha Wildner device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
6935db2f26eSSascha Wildner bios_irq, (int)bus, slot, pin + 'A');
6945db2f26eSSascha Wildner } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
6955db2f26eSSascha Wildner link->l_bios_irq = bios_irq;
6965db2f26eSSascha Wildner /*
6975db2f26eSSascha Wildner * SCI setting is handled by acpi_pci_link_identify()
6985db2f26eSSascha Wildner */
6995db2f26eSSascha Wildner if (bios_irq < NUM_ISA_INTERRUPTS &&
7005db2f26eSSascha Wildner AcpiGbl_FADT.SciInterrupt != bios_irq)
7015db2f26eSSascha Wildner pci_link_bios_isa_irqs |= (1 << bios_irq);
7025db2f26eSSascha Wildner if (bios_irq != link->l_initial_irq &&
7035db2f26eSSascha Wildner PCI_INTERRUPT_VALID(link->l_initial_irq))
7045db2f26eSSascha Wildner device_printf(dev,
7055db2f26eSSascha Wildner "BIOS IRQ %u does not match initial IRQ %u\n",
7065db2f26eSSascha Wildner bios_irq, link->l_initial_irq);
7075db2f26eSSascha Wildner } else if (bios_irq != link->l_bios_irq)
7085db2f26eSSascha Wildner device_printf(dev,
7095db2f26eSSascha Wildner "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
7105db2f26eSSascha Wildner bios_irq, (int)bus, slot, pin + 'A',
7115db2f26eSSascha Wildner link->l_bios_irq);
7125db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
7135db2f26eSSascha Wildner }
7145db2f26eSSascha Wildner
7155db2f26eSSascha Wildner static ACPI_STATUS
acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc * sc,ACPI_BUFFER * srsbuf)7165db2f26eSSascha Wildner acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
7175db2f26eSSascha Wildner {
71806871c60SSascha Wildner ACPI_RESOURCE *end, *res;
7195db2f26eSSascha Wildner ACPI_STATUS status;
7205db2f26eSSascha Wildner struct link *link;
7215db2f26eSSascha Wildner int i, in_dpf;
7225db2f26eSSascha Wildner
7235db2f26eSSascha Wildner /* Fetch the _CRS. */
7245db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
72506871c60SSascha Wildner srsbuf->Pointer = NULL;
72606871c60SSascha Wildner srsbuf->Length = ACPI_ALLOCATE_BUFFER;
72706871c60SSascha Wildner status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), srsbuf);
72806871c60SSascha Wildner if (ACPI_SUCCESS(status) && srsbuf->Pointer == NULL)
7295db2f26eSSascha Wildner status = AE_NO_MEMORY;
7305db2f26eSSascha Wildner if (ACPI_FAILURE(status)) {
7315db2f26eSSascha Wildner if (bootverbose)
7325db2f26eSSascha Wildner device_printf(sc->pl_dev,
7335db2f26eSSascha Wildner "Unable to fetch current resources: %s\n",
7345db2f26eSSascha Wildner AcpiFormatException(status));
7355db2f26eSSascha Wildner return (status);
7365db2f26eSSascha Wildner }
7375db2f26eSSascha Wildner
7385db2f26eSSascha Wildner /* Fill in IRQ resources via link structures. */
7395db2f26eSSascha Wildner link = sc->pl_links;
7405db2f26eSSascha Wildner i = 0;
7415db2f26eSSascha Wildner in_dpf = DPF_OUTSIDE;
74206871c60SSascha Wildner res = (ACPI_RESOURCE *)srsbuf->Pointer;
74306871c60SSascha Wildner end = (ACPI_RESOURCE *)((char *)srsbuf->Pointer + srsbuf->Length);
7445db2f26eSSascha Wildner for (;;) {
74506871c60SSascha Wildner switch (res->Type) {
7465db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_START_DEPENDENT:
7475db2f26eSSascha Wildner switch (in_dpf) {
7485db2f26eSSascha Wildner case DPF_OUTSIDE:
7495db2f26eSSascha Wildner /* We've started the first DPF. */
7505db2f26eSSascha Wildner in_dpf = DPF_FIRST;
7515db2f26eSSascha Wildner break;
7525db2f26eSSascha Wildner case DPF_FIRST:
7535db2f26eSSascha Wildner /* We've started the second DPF. */
7545db2f26eSSascha Wildner panic(
7555db2f26eSSascha Wildner "%s: Multiple dependent functions within a current resource",
7565db2f26eSSascha Wildner __func__);
7575db2f26eSSascha Wildner break;
7585db2f26eSSascha Wildner }
7595db2f26eSSascha Wildner break;
7605db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_END_DEPENDENT:
7615db2f26eSSascha Wildner /* We are finished with DPF parsing. */
7625db2f26eSSascha Wildner KASSERT(in_dpf != DPF_OUTSIDE,
7635db2f26eSSascha Wildner ("%s: end dpf when not parsing a dpf", __func__));
7645db2f26eSSascha Wildner in_dpf = DPF_OUTSIDE;
7655db2f26eSSascha Wildner break;
7665db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_IRQ:
7675db2f26eSSascha Wildner KKASSERT(i < sc->pl_num_links);
76806871c60SSascha Wildner res->Data.Irq.InterruptCount = 1;
7695db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_irq)) {
7705db2f26eSSascha Wildner KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
7715db2f26eSSascha Wildner ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
7725db2f26eSSascha Wildner __func__, link->l_irq));
77306871c60SSascha Wildner res->Data.Irq.Interrupts[0] = link->l_irq;
7745db2f26eSSascha Wildner } else
77506871c60SSascha Wildner res->Data.Irq.Interrupts[0] = 0;
7765db2f26eSSascha Wildner link++;
7775db2f26eSSascha Wildner i++;
7785db2f26eSSascha Wildner break;
7795db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
7805db2f26eSSascha Wildner KKASSERT(i < sc->pl_num_links);
78106871c60SSascha Wildner res->Data.ExtendedIrq.InterruptCount = 1;
7825db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_irq))
78306871c60SSascha Wildner res->Data.ExtendedIrq.Interrupts[0] =
7845db2f26eSSascha Wildner link->l_irq;
7855db2f26eSSascha Wildner else
78606871c60SSascha Wildner res->Data.ExtendedIrq.Interrupts[0] = 0;
7875db2f26eSSascha Wildner link++;
7885db2f26eSSascha Wildner i++;
7895db2f26eSSascha Wildner break;
7905db2f26eSSascha Wildner }
79106871c60SSascha Wildner if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
7925db2f26eSSascha Wildner break;
79306871c60SSascha Wildner res = ACPI_NEXT_RESOURCE(res);
79406871c60SSascha Wildner if (res >= end)
7955db2f26eSSascha Wildner break;
7965db2f26eSSascha Wildner }
7975db2f26eSSascha Wildner return (AE_OK);
7985db2f26eSSascha Wildner }
7995db2f26eSSascha Wildner
8005db2f26eSSascha Wildner static ACPI_STATUS
acpi_pci_link_srs_from_links(struct acpi_pci_link_softc * sc,ACPI_BUFFER * srsbuf)8015db2f26eSSascha Wildner acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
8025db2f26eSSascha Wildner ACPI_BUFFER *srsbuf)
8035db2f26eSSascha Wildner {
8045db2f26eSSascha Wildner ACPI_RESOURCE newres;
8055db2f26eSSascha Wildner ACPI_STATUS status;
8065db2f26eSSascha Wildner struct link *link;
8075db2f26eSSascha Wildner int i;
8085db2f26eSSascha Wildner
8095db2f26eSSascha Wildner /* Start off with an empty buffer. */
8105db2f26eSSascha Wildner srsbuf->Pointer = NULL;
8115db2f26eSSascha Wildner link = sc->pl_links;
8125db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++) {
8135db2f26eSSascha Wildner
8145db2f26eSSascha Wildner /* Add a new IRQ resource from each link. */
8155db2f26eSSascha Wildner link = &sc->pl_links[i];
81606871c60SSascha Wildner if (link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ) {
8175db2f26eSSascha Wildner
8185db2f26eSSascha Wildner /* Build an IRQ resource. */
81906871c60SSascha Wildner bcopy(&link->l_prs_template, &newres,
82006871c60SSascha Wildner ACPI_RS_SIZE(newres.Data.Irq));
8215db2f26eSSascha Wildner newres.Data.Irq.InterruptCount = 1;
8225db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_irq)) {
8235db2f26eSSascha Wildner KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
8245db2f26eSSascha Wildner ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
8255db2f26eSSascha Wildner __func__, link->l_irq));
8265db2f26eSSascha Wildner newres.Data.Irq.Interrupts[0] = link->l_irq;
8275db2f26eSSascha Wildner } else
8285db2f26eSSascha Wildner newres.Data.Irq.Interrupts[0] = 0;
8295db2f26eSSascha Wildner } else {
8305db2f26eSSascha Wildner
8315db2f26eSSascha Wildner /* Build an ExtIRQ resuorce. */
83206871c60SSascha Wildner bcopy(&link->l_prs_template, &newres,
83306871c60SSascha Wildner ACPI_RS_SIZE(newres.Data.ExtendedIrq));
8345db2f26eSSascha Wildner newres.Data.ExtendedIrq.InterruptCount = 1;
8355db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_irq))
8365db2f26eSSascha Wildner newres.Data.ExtendedIrq.Interrupts[0] =
8375db2f26eSSascha Wildner link->l_irq;
8385db2f26eSSascha Wildner else
8395db2f26eSSascha Wildner newres.Data.ExtendedIrq.Interrupts[0] = 0;
8405db2f26eSSascha Wildner }
8415db2f26eSSascha Wildner
8425db2f26eSSascha Wildner /* Add the new resource to the end of the _SRS buffer. */
8435db2f26eSSascha Wildner status = acpi_AppendBufferResource(srsbuf, &newres);
8445db2f26eSSascha Wildner if (ACPI_FAILURE(status)) {
8455db2f26eSSascha Wildner device_printf(sc->pl_dev,
8465db2f26eSSascha Wildner "Unable to build resources: %s\n",
8475db2f26eSSascha Wildner AcpiFormatException(status));
8485db2f26eSSascha Wildner if (srsbuf->Pointer != NULL)
8495db2f26eSSascha Wildner AcpiOsFree(srsbuf->Pointer);
8505db2f26eSSascha Wildner return (status);
8515db2f26eSSascha Wildner }
8525db2f26eSSascha Wildner }
8535db2f26eSSascha Wildner return (AE_OK);
8545db2f26eSSascha Wildner }
8555db2f26eSSascha Wildner
8565db2f26eSSascha Wildner static ACPI_STATUS
acpi_pci_link_route_irqs(device_t dev)8575db2f26eSSascha Wildner acpi_pci_link_route_irqs(device_t dev)
8585db2f26eSSascha Wildner {
8595db2f26eSSascha Wildner struct acpi_pci_link_softc *sc;
8605db2f26eSSascha Wildner ACPI_RESOURCE *resource, *end;
8615db2f26eSSascha Wildner ACPI_BUFFER srsbuf;
8625db2f26eSSascha Wildner ACPI_STATUS status;
8635db2f26eSSascha Wildner struct link *link;
8645db2f26eSSascha Wildner int i;
8655db2f26eSSascha Wildner
8665db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(pci_link);
8675db2f26eSSascha Wildner sc = device_get_softc(dev);
8685db2f26eSSascha Wildner if (sc->pl_crs_bad)
8695db2f26eSSascha Wildner status = acpi_pci_link_srs_from_links(sc, &srsbuf);
8705db2f26eSSascha Wildner else
8715db2f26eSSascha Wildner status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
8725db2f26eSSascha Wildner
8735db2f26eSSascha Wildner /* Write out new resources via _SRS. */
8745db2f26eSSascha Wildner status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
8755db2f26eSSascha Wildner if (ACPI_FAILURE(status)) {
8765db2f26eSSascha Wildner device_printf(dev, "Unable to route IRQs: %s\n",
8775db2f26eSSascha Wildner AcpiFormatException(status));
8785db2f26eSSascha Wildner AcpiOsFree(srsbuf.Pointer);
8795db2f26eSSascha Wildner return (status);
8805db2f26eSSascha Wildner }
8815db2f26eSSascha Wildner
8825db2f26eSSascha Wildner /*
8835db2f26eSSascha Wildner * Perform acpi_config_intr() on each IRQ resource if it was just
8845db2f26eSSascha Wildner * routed for the first time.
8855db2f26eSSascha Wildner */
8865db2f26eSSascha Wildner link = sc->pl_links;
8875db2f26eSSascha Wildner i = 0;
8885db2f26eSSascha Wildner resource = (ACPI_RESOURCE *)srsbuf.Pointer;
8895db2f26eSSascha Wildner end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
8905db2f26eSSascha Wildner for (;;) {
8915db2f26eSSascha Wildner if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
8925db2f26eSSascha Wildner break;
8935db2f26eSSascha Wildner switch (resource->Type) {
8945db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_IRQ:
8955db2f26eSSascha Wildner case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
8965db2f26eSSascha Wildner KKASSERT(i < sc->pl_num_links);
8975db2f26eSSascha Wildner
8985db2f26eSSascha Wildner /*
8995db2f26eSSascha Wildner * Only configure the interrupt and update the
9005db2f26eSSascha Wildner * weights if this link has a valid IRQ and was
9015db2f26eSSascha Wildner * previously unrouted.
9025db2f26eSSascha Wildner */
9035db2f26eSSascha Wildner if (!link->l_routed &&
9045db2f26eSSascha Wildner PCI_INTERRUPT_VALID(link->l_irq)) {
9055db2f26eSSascha Wildner link->l_routed = TRUE;
9065db2f26eSSascha Wildner acpi_config_intr(dev, resource);
9075db2f26eSSascha Wildner pci_link_interrupt_weights[link->l_irq] +=
9085db2f26eSSascha Wildner link->l_references;
9095db2f26eSSascha Wildner }
9105db2f26eSSascha Wildner link++;
9115db2f26eSSascha Wildner i++;
9125db2f26eSSascha Wildner break;
9135db2f26eSSascha Wildner }
9145db2f26eSSascha Wildner resource = ACPI_NEXT_RESOURCE(resource);
9155db2f26eSSascha Wildner if (resource >= end)
9165db2f26eSSascha Wildner break;
9175db2f26eSSascha Wildner }
9185db2f26eSSascha Wildner AcpiOsFree(srsbuf.Pointer);
9195db2f26eSSascha Wildner return (AE_OK);
9205db2f26eSSascha Wildner }
9215db2f26eSSascha Wildner
9225db2f26eSSascha Wildner static int
acpi_pci_link_resume(device_t dev)9235db2f26eSSascha Wildner acpi_pci_link_resume(device_t dev)
9245db2f26eSSascha Wildner {
9255db2f26eSSascha Wildner struct acpi_pci_link_softc *sc;
9265db2f26eSSascha Wildner ACPI_STATUS status;
9275db2f26eSSascha Wildner int i, routed;
9285db2f26eSSascha Wildner
9295db2f26eSSascha Wildner /*
9305db2f26eSSascha Wildner * If all of our links are routed, then restore the link via _SRS,
9315db2f26eSSascha Wildner * otherwise, disable the link via _DIS.
9325db2f26eSSascha Wildner */
9335db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(pci_link);
9345db2f26eSSascha Wildner sc = device_get_softc(dev);
9355db2f26eSSascha Wildner routed = 0;
9365db2f26eSSascha Wildner for (i = 0; i < sc->pl_num_links; i++)
9375db2f26eSSascha Wildner if (sc->pl_links[i].l_routed)
9385db2f26eSSascha Wildner routed++;
9395db2f26eSSascha Wildner if (routed == sc->pl_num_links)
9405db2f26eSSascha Wildner status = acpi_pci_link_route_irqs(dev);
9415db2f26eSSascha Wildner else {
9425db2f26eSSascha Wildner AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL);
9435db2f26eSSascha Wildner status = AE_OK;
9445db2f26eSSascha Wildner }
9455db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
9465db2f26eSSascha Wildner if (ACPI_FAILURE(status))
9475db2f26eSSascha Wildner return (ENXIO);
9485db2f26eSSascha Wildner else
9495db2f26eSSascha Wildner return (0);
9505db2f26eSSascha Wildner }
9515db2f26eSSascha Wildner
9525db2f26eSSascha Wildner /*
9535db2f26eSSascha Wildner * Pick an IRQ to use for this unrouted link.
9545db2f26eSSascha Wildner */
9555db2f26eSSascha Wildner static uint8_t
acpi_pci_link_choose_irq(device_t dev,struct link * link)9565db2f26eSSascha Wildner acpi_pci_link_choose_irq(device_t dev, struct link *link)
9575db2f26eSSascha Wildner {
9585db2f26eSSascha Wildner char tunable_buffer[64], link_name[5];
9593905511eSSascha Wildner uint8_t best_irq, pos_irq;
9605db2f26eSSascha Wildner int best_weight, pos_weight, i;
9615db2f26eSSascha Wildner
9625db2f26eSSascha Wildner KASSERT(!link->l_routed, ("%s: link already routed", __func__));
9635db2f26eSSascha Wildner KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
9645db2f26eSSascha Wildner ("%s: link already has an IRQ", __func__));
9655db2f26eSSascha Wildner
9665db2f26eSSascha Wildner /* Check for a tunable override. */
9675db2f26eSSascha Wildner if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
9685db2f26eSSascha Wildner sizeof(link_name)))) {
9695db2f26eSSascha Wildner ksnprintf(tunable_buffer, sizeof(tunable_buffer),
9705db2f26eSSascha Wildner "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
9715db2f26eSSascha Wildner if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
9725db2f26eSSascha Wildner if (!link_valid_irq(link, i))
9735db2f26eSSascha Wildner device_printf(dev,
9745db2f26eSSascha Wildner "Warning, IRQ %d is not listed as valid\n",
9755db2f26eSSascha Wildner i);
9765db2f26eSSascha Wildner return (i);
9775db2f26eSSascha Wildner }
9785db2f26eSSascha Wildner ksnprintf(tunable_buffer, sizeof(tunable_buffer),
9795db2f26eSSascha Wildner "hw.pci.link.%s.irq", link_name);
9805db2f26eSSascha Wildner if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
9815db2f26eSSascha Wildner if (!link_valid_irq(link, i))
9825db2f26eSSascha Wildner device_printf(dev,
9835db2f26eSSascha Wildner "Warning, IRQ %d is not listed as valid\n",
9845db2f26eSSascha Wildner i);
9855db2f26eSSascha Wildner return (i);
9865db2f26eSSascha Wildner }
9875db2f26eSSascha Wildner }
9885db2f26eSSascha Wildner
9895db2f26eSSascha Wildner /*
9905db2f26eSSascha Wildner * If we have a valid BIOS IRQ, use that. We trust what the BIOS
9915db2f26eSSascha Wildner * says it routed over what _CRS says the link thinks is routed.
9925db2f26eSSascha Wildner */
9935db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_bios_irq))
9945db2f26eSSascha Wildner return (link->l_bios_irq);
9955db2f26eSSascha Wildner
9965db2f26eSSascha Wildner /*
9975db2f26eSSascha Wildner * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
9985db2f26eSSascha Wildner * then use that.
9995db2f26eSSascha Wildner */
10005db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_initial_irq))
10015db2f26eSSascha Wildner return (link->l_initial_irq);
10025db2f26eSSascha Wildner
10035db2f26eSSascha Wildner /*
10045db2f26eSSascha Wildner * Ok, we have no useful hints, so we have to pick from the
10055db2f26eSSascha Wildner * possible IRQs. For ISA IRQs we only use interrupts that
10065db2f26eSSascha Wildner * have already been used by the BIOS.
10075db2f26eSSascha Wildner */
10085db2f26eSSascha Wildner best_irq = PCI_INVALID_IRQ;
10095db2f26eSSascha Wildner best_weight = INT_MAX;
10105db2f26eSSascha Wildner for (i = 0; i < link->l_num_irqs; i++) {
10115db2f26eSSascha Wildner pos_irq = link->l_irqs[i];
10125db2f26eSSascha Wildner if (pos_irq < NUM_ISA_INTERRUPTS &&
10135db2f26eSSascha Wildner (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
10145db2f26eSSascha Wildner continue;
10155db2f26eSSascha Wildner pos_weight = pci_link_interrupt_weights[pos_irq];
10165db2f26eSSascha Wildner if (pos_weight < best_weight) {
10175db2f26eSSascha Wildner best_weight = pos_weight;
10185db2f26eSSascha Wildner best_irq = pos_irq;
10195db2f26eSSascha Wildner }
10205db2f26eSSascha Wildner }
10215db2f26eSSascha Wildner
10225db2f26eSSascha Wildner /*
10235db2f26eSSascha Wildner * If this is an ISA IRQ and SCI could be shared, try using
10245db2f26eSSascha Wildner * the SCI as a fallback.
10255db2f26eSSascha Wildner */
1026737d9e74SSascha Wildner if (link->l_isa_irq && acpi_sci_pci_shareable()) {
10275db2f26eSSascha Wildner pos_irq = AcpiGbl_FADT.SciInterrupt;
10285db2f26eSSascha Wildner pos_weight = pci_link_interrupt_weights[pos_irq];
10295db2f26eSSascha Wildner if (pos_weight < best_weight) {
10305db2f26eSSascha Wildner best_weight = pos_weight;
10315db2f26eSSascha Wildner best_irq = pos_irq;
10325db2f26eSSascha Wildner }
10335db2f26eSSascha Wildner }
10345db2f26eSSascha Wildner
10355db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(best_irq)) {
10365db2f26eSSascha Wildner if (bootverbose)
10375db2f26eSSascha Wildner device_printf(dev, "Picked IRQ %u with weight %d\n",
10385db2f26eSSascha Wildner best_irq, best_weight);
10395db2f26eSSascha Wildner } else
10405db2f26eSSascha Wildner device_printf(dev, "Unable to choose an IRQ\n");
10415db2f26eSSascha Wildner return (best_irq);
10425db2f26eSSascha Wildner }
10435db2f26eSSascha Wildner
10445db2f26eSSascha Wildner int
acpi_pci_link_route_interrupt(device_t dev,int index)10455db2f26eSSascha Wildner acpi_pci_link_route_interrupt(device_t dev, int index)
10465db2f26eSSascha Wildner {
10475db2f26eSSascha Wildner struct link *link;
10485db2f26eSSascha Wildner
10495db2f26eSSascha Wildner if (acpi_disabled("pci_link"))
10505db2f26eSSascha Wildner return (PCI_INVALID_IRQ);
10515db2f26eSSascha Wildner
10525db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(pci_link);
10535db2f26eSSascha Wildner link = acpi_pci_link_lookup(dev, index);
10545db2f26eSSascha Wildner if (link == NULL)
10555db2f26eSSascha Wildner panic("%s: apparently invalid index %d", __func__, index);
10565db2f26eSSascha Wildner
10575db2f26eSSascha Wildner /*
10585db2f26eSSascha Wildner * If this link device is already routed to an interrupt, just return
10595db2f26eSSascha Wildner * the interrupt it is routed to.
10605db2f26eSSascha Wildner */
10615db2f26eSSascha Wildner if (link->l_routed) {
10625db2f26eSSascha Wildner KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
10635db2f26eSSascha Wildner ("%s: link is routed but has an invalid IRQ", __func__));
10645db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
10655db2f26eSSascha Wildner return (link->l_irq);
10665db2f26eSSascha Wildner }
10675db2f26eSSascha Wildner
10685db2f26eSSascha Wildner /* Choose an IRQ if we need one. */
10695db2f26eSSascha Wildner if (!PCI_INTERRUPT_VALID(link->l_irq)) {
10705db2f26eSSascha Wildner link->l_irq = acpi_pci_link_choose_irq(dev, link);
10715db2f26eSSascha Wildner
10725db2f26eSSascha Wildner /*
10735db2f26eSSascha Wildner * Try to route the interrupt we picked. If it fails, then
10745db2f26eSSascha Wildner * assume the interrupt is not routed.
10755db2f26eSSascha Wildner */
10765db2f26eSSascha Wildner if (PCI_INTERRUPT_VALID(link->l_irq)) {
10775db2f26eSSascha Wildner acpi_pci_link_route_irqs(dev);
10785db2f26eSSascha Wildner if (!link->l_routed)
10795db2f26eSSascha Wildner link->l_irq = PCI_INVALID_IRQ;
10805db2f26eSSascha Wildner }
10815db2f26eSSascha Wildner }
10825db2f26eSSascha Wildner ACPI_SERIAL_END(pci_link);
10835db2f26eSSascha Wildner return (link->l_irq);
10845db2f26eSSascha Wildner }
10855db2f26eSSascha Wildner
10865db2f26eSSascha Wildner /*
10875db2f26eSSascha Wildner * This is gross, but we abuse the identify routine to perform one-time
10885db2f26eSSascha Wildner * SYSINIT() style initialization for the driver.
10895db2f26eSSascha Wildner */
10905db2f26eSSascha Wildner static void
acpi_pci_link_identify(driver_t * driver,device_t parent)10915db2f26eSSascha Wildner acpi_pci_link_identify(driver_t *driver, device_t parent)
10925db2f26eSSascha Wildner {
10935db2f26eSSascha Wildner /*
10945db2f26eSSascha Wildner * If the SCI is an ISA IRQ and could be shared,
10955db2f26eSSascha Wildner * add it to the bitmask of known good ISA IRQs.
10965db2f26eSSascha Wildner */
10975db2f26eSSascha Wildner if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS &&
1098737d9e74SSascha Wildner acpi_sci_pci_shareable())
10995db2f26eSSascha Wildner pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt);
11005db2f26eSSascha Wildner }
11015db2f26eSSascha Wildner
11025db2f26eSSascha Wildner static device_method_t acpi_pci_link_methods[] = {
11035db2f26eSSascha Wildner /* Device interface */
11045db2f26eSSascha Wildner DEVMETHOD(device_identify, acpi_pci_link_identify),
11055db2f26eSSascha Wildner DEVMETHOD(device_probe, acpi_pci_link_probe),
11065db2f26eSSascha Wildner DEVMETHOD(device_attach, acpi_pci_link_attach),
11075db2f26eSSascha Wildner DEVMETHOD(device_resume, acpi_pci_link_resume),
11085db2f26eSSascha Wildner
1109d3c9c58eSSascha Wildner DEVMETHOD_END
11105db2f26eSSascha Wildner };
11115db2f26eSSascha Wildner
11125db2f26eSSascha Wildner static driver_t acpi_pci_link_driver = {
11135db2f26eSSascha Wildner "pci_link",
11145db2f26eSSascha Wildner acpi_pci_link_methods,
11155db2f26eSSascha Wildner sizeof(struct acpi_pci_link_softc),
1116*5025fc65SMatthew Dillon .gpri = KOBJ_GPRI_ACPI
11175db2f26eSSascha Wildner };
11185db2f26eSSascha Wildner
11195db2f26eSSascha Wildner static devclass_t pci_link_devclass;
11205db2f26eSSascha Wildner
11215db2f26eSSascha Wildner DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, pci_link_devclass,
11225db2f26eSSascha Wildner NULL, NULL);
11235db2f26eSSascha Wildner MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1);
1124