Bring in acpi_pci_* from FreeBSD 7.2
authorAlexander Polakov <polachok@gmail.com>
Wed, 14 Oct 2009 19:11:34 +0000 (23:11 +0400)
committerAlexander Polakov <polachok@gmail.com>
Wed, 14 Oct 2009 19:11:34 +0000 (23:11 +0400)
sys/dev/acpica5/acpi_pci.c
sys/dev/acpica5/acpi_pci_link.c
sys/dev/acpica5/acpi_pcib.c
sys/dev/acpica5/acpi_pcib_acpi.c
sys/dev/acpica5/acpi_pcib_pci.c
sys/dev/acpica5/acpi_pcibvar.h

index 5f8e162..895d6e0 100644 (file)
@@ -1,6 +1,6 @@
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org>
+ * Copyright (c) 2000, Michael Smith <msmith@kfreebsd.org>
  * Copyright (c) 2000, BSDi
  * All rights reserved.
  *
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/dev/acpica/acpi_pci.c,v 1.16 2004/05/29 04:32:50 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pci.c,v 1.5 2006/09/05 00:55:36 dillon Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pci.c,v 1.31.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $");
  */
 
-#include "opt_bus.h"
+#include <sys/cdefs.h>
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -39,7 +37,6 @@
 #include <sys/module.h>
 
 #include "acpi.h"
-#include "accommon.h"
 #include "acpivar.h"
 
 #include <sys/pciio.h>
@@ -57,73 +54,84 @@ ACPI_MODULE_NAME("PCI")
 struct acpi_pci_devinfo {
        struct pci_devinfo      ap_dinfo;
        ACPI_HANDLE             ap_handle;
+       int                     ap_flags;
 };
 
-static int     acpi_pci_probe(device_t dev);
+ACPI_SERIAL_DECL(pci_powerstate, "ACPI PCI power methods");
+
+/* Be sure that ACPI and PCI power states are equivalent. */
+CTASSERT(ACPI_STATE_D0 == PCI_POWERSTATE_D0);
+CTASSERT(ACPI_STATE_D1 == PCI_POWERSTATE_D1);
+CTASSERT(ACPI_STATE_D2 == PCI_POWERSTATE_D2);
+CTASSERT(ACPI_STATE_D3 == PCI_POWERSTATE_D3);
+
 static int     acpi_pci_attach(device_t dev);
-static int     acpi_pci_read_ivar(device_t dev, device_t child, int which,
-                   uintptr_t *result);
+static int     acpi_pci_suspend(device_t dev);
+static int     acpi_pci_resume(device_t dev);
 static int     acpi_pci_child_location_str_method(device_t cbdev,
                    device_t child, char *buf, size_t buflen);
-
-
-static int     acpi_pci_set_powerstate_method(device_t dev, device_t child,
-                   int state);
+static int     acpi_pci_probe(device_t dev);
+static int     acpi_pci_read_ivar(device_t dev, device_t child, int which,
+                   uintptr_t *result);
+static int     acpi_pci_write_ivar(device_t dev, device_t child, int which,
+                   uintptr_t value);
 static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level,
                    void *context, void **status);
+static int     acpi_pci_set_powerstate_method(device_t dev, device_t child,
+                   int state);
+static void    acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child);
 
 static device_method_t acpi_pci_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,         acpi_pci_probe),
        DEVMETHOD(device_attach,        acpi_pci_attach),
-       DEVMETHOD(device_shutdown,      bus_generic_shutdown),
-       DEVMETHOD(device_suspend,       bus_generic_suspend),
-       DEVMETHOD(device_resume,        bus_generic_resume),
+        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
+        DEVMETHOD(device_suspend,       acpi_pci_suspend),
+        DEVMETHOD(device_resume,        acpi_pci_resume),
 
        /* Bus interface */
-       DEVMETHOD(bus_print_child,      pci_print_child),
-       DEVMETHOD(bus_probe_nomatch,    pci_probe_nomatch),
+       DEVMETHOD(bus_print_child,      pci_print_child),
+        DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
+        DEVMETHOD(bus_set_resource,     bus_generic_rl_set_resource),
+        DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
+        DEVMETHOD(bus_delete_resource,  pci_delete_resource),
+        DEVMETHOD(bus_alloc_resource,   pci_alloc_resource),
+        DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+        DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+        DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
        DEVMETHOD(bus_read_ivar,        acpi_pci_read_ivar),
-       DEVMETHOD(bus_write_ivar,       pci_write_ivar),
-       DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
-       DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
-       DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
-
-       DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
-       DEVMETHOD(bus_set_resource,     bus_generic_rl_set_resource),
-       DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
-       DEVMETHOD(bus_delete_resource,  pci_delete_resource),
-       DEVMETHOD(bus_alloc_resource,   pci_alloc_resource),
-       DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
-       DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
-       DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
-       DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
+       DEVMETHOD(bus_write_ivar,       acpi_pci_write_ivar),
+        DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+        DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
+        DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
        DEVMETHOD(bus_child_location_str, acpi_pci_child_location_str_method),
 
        /* PCI interface */
-       DEVMETHOD(pci_read_config,      pci_read_config_method),
-       DEVMETHOD(pci_write_config,     pci_write_config_method),
-       DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method),
-       DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
-       DEVMETHOD(pci_enable_io,        pci_enable_io_method),
-       DEVMETHOD(pci_disable_io,       pci_disable_io_method),
-       DEVMETHOD(pci_get_powerstate,   pci_get_powerstate_method),
        DEVMETHOD(pci_set_powerstate,   acpi_pci_set_powerstate_method),
-       DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
+        DEVMETHOD(pci_read_config,      pci_read_config_method),
+        DEVMETHOD(pci_write_config,     pci_write_config_method),
+        DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method),
+        DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
+        DEVMETHOD(pci_enable_io,        pci_enable_io_method),
+        DEVMETHOD(pci_disable_io,       pci_disable_io_method),
+        DEVMETHOD(pci_get_powerstate,   pci_get_powerstate_method),
+        DEVMETHOD(pci_set_powerstate,   acpi_pci_set_powerstate_method),
+        DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
 
        { 0, 0 }
 };
 
 static driver_t acpi_pci_driver = {
-       "pci",
-       acpi_pci_methods,
-       0,                      /* no softc */
+        "pci",
+        acpi_pci_methods,
+        0,                      /* no softc */
 };
 
 static devclass_t pci_devclass;
 
 DRIVER_MODULE(acpi_pci, pcib, acpi_pci_driver, pci_devclass, 0, 0);
 MODULE_DEPEND(acpi_pci, acpi, 1, 1, 1);
+MODULE_DEPEND(acpi_pci, pci, 1, 1, 1);
 MODULE_VERSION(acpi_pci, 1);
 
 static int
@@ -131,16 +139,36 @@ acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
 {
     struct acpi_pci_devinfo *dinfo;
 
+    dinfo = device_get_ivars(child);
     switch (which) {
     case ACPI_IVAR_HANDLE:
-       dinfo = device_get_ivars(child);
        *result = (uintptr_t)dinfo->ap_handle;
        return (0);
+    case ACPI_IVAR_FLAGS:
+       *result = (uintptr_t)dinfo->ap_flags;
+       return (0);
     }
     return (pci_read_ivar(dev, child, which, result));
 }
 
 static int
+acpi_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+    struct acpi_pci_devinfo *dinfo;
+
+    dinfo = device_get_ivars(child);
+    switch (which) {
+    case ACPI_IVAR_HANDLE:
+       dinfo->ap_handle = (ACPI_HANDLE)value;
+       return (0);
+    case ACPI_IVAR_FLAGS:
+       dinfo->ap_flags = (int)value;
+       return (0);
+    }
+    return (pci_write_ivar(dev, child, which, value));
+}
+
+static int
 acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf,
     size_t buflen)
 {
@@ -149,7 +177,7 @@ acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf,
     pci_child_location_str_method(cbdev, child, buf, buflen);
 
     if (dinfo->ap_handle) {
-       strlcat(buf, " path=", buflen);
+       strlcat(buf, " handle=", buflen);
        strlcat(buf, acpi_name(dinfo->ap_handle), buflen);
     }
     return (0);
@@ -163,24 +191,11 @@ acpi_pci_set_powerstate_method(device_t dev, device_t child, int state)
 {
        ACPI_HANDLE h;
        ACPI_STATUS status;
-       int acpi_state, old_state, error;
-
-       switch (state) {
-       case PCI_POWERSTATE_D0:
-               acpi_state = ACPI_STATE_D0;
-               break;
-       case PCI_POWERSTATE_D1:
-               acpi_state = ACPI_STATE_D1;
-               break;
-       case PCI_POWERSTATE_D2:
-               acpi_state = ACPI_STATE_D2;
-               break;
-       case PCI_POWERSTATE_D3:
-               acpi_state = ACPI_STATE_D3;
-               break;
-       default:
+       int old_state, error;
+
+       error = 0;
+       if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3)
                return (EINVAL);
-       }
 
        /*
         * We set the state using PCI Power Management outside of setting
@@ -191,25 +206,70 @@ acpi_pci_set_powerstate_method(device_t dev, device_t child, int state)
         * it can enable any needed Power Resources before changing the PCI
         * power state.
         */
+       ACPI_SERIAL_BEGIN(pci_powerstate);
        old_state = pci_get_powerstate(child);
        if (old_state < state) {
                error = pci_set_powerstate_method(dev, child, state);
                if (error)
-                       return (error);
+                       goto out;
        }
        h = acpi_get_handle(child);
-       if (h != NULL) {
-               status = acpi_pwr_switch_consumer(h, acpi_state);
-               if (ACPI_FAILURE(status))
-                       device_printf(dev,
-                           "Failed to set ACPI power state D%d on %s: %s\n",
-                           acpi_state, device_get_nameunit(child),
-                           AcpiFormatException(status));
-       }
+       status = acpi_pwr_switch_consumer(h, state);
+       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+               device_printf(dev,
+                   "Failed to set ACPI power state D%d on %s: %s\n",
+                   state, acpi_name(h), AcpiFormatException(status));
        if (old_state > state)
-               return (pci_set_powerstate_method(dev, child, state));
-       else
-               return (0);
+               error = pci_set_powerstate_method(dev, child, state);
+
+out:
+       ACPI_SERIAL_END(pci_powerstate);
+       return (error);
+}
+
+static void
+acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child)
+{
+       ACPI_STATUS status;
+       device_t child;
+
+       /*
+        * Lookup and remove the unused device that acpi0 creates when it walks
+        * the namespace creating devices.
+        */
+       child = acpi_get_device(handle);
+       if (child != NULL) {
+               if (device_is_alive(child)) {
+                       /*
+                        * The TabletPC TC1000 has a second PCI-ISA bridge
+                        * that has a _HID for an acpi_sysresource device.
+                        * In that case, leave ACPI-CA's device data pointing
+                        * at the ACPI-enumerated device.
+                        */
+                       device_printf(child,
+                           "Conflicts with PCI device %d:%d:%d\n",
+                           pci_get_bus(pci_child), pci_get_slot(pci_child),
+                           pci_get_function(pci_child));
+                       return;
+               }
+               KASSERT(device_get_parent(child) ==
+                   devclass_get_device(devclass_find("acpi"), 0),
+                   ("%s: child (%s)'s parent is not acpi0", __func__,
+                   acpi_name(handle)));
+               device_delete_child(device_get_parent(child), child);
+       }
+
+       /*
+        * Update ACPI-CA to use the PCI enumerated device_t for this handle.
+        */
+       status = AcpiDetachData(handle, acpi_fake_objhandler);
+       if (ACPI_FAILURE(status))
+               kprintf("WARNING: Unable to detach object data from %s - %s\n",
+                   acpi_name(handle), AcpiFormatException(status));
+       status = AcpiAttachData(handle, acpi_fake_objhandler, pci_child);
+       if (ACPI_FAILURE(status))
+               kprintf("WARNING: Unable to attach object data to %s - %s\n",
+                   acpi_name(handle), AcpiFormatException(status));
 }
 
 static ACPI_STATUS
@@ -225,8 +285,8 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context,
 
        if (ACPI_FAILURE(acpi_GetInteger(handle, "_ADR", &address)))
                return_ACPI_STATUS (AE_OK);
-       slot = address >> 16;
-       func = address & 0xffff;
+       slot = ACPI_ADR_PCI_SLOT(address);
+       func = ACPI_ADR_PCI_FUNC(address);
        if (device_get_children((device_t)context, &devlist, &devcount) != 0)
                return_ACPI_STATUS (AE_OK);
        for (i = 0; i < devcount; i++) {
@@ -234,8 +294,8 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context,
                if (dinfo->ap_dinfo.cfg.func == func &&
                    dinfo->ap_dinfo.cfg.slot == slot) {
                        dinfo->ap_handle = handle;
-                       kfree(devlist, M_TEMP);
-                       return_ACPI_STATUS (AE_OK);
+                       acpi_pci_update_device(handle, devlist[i]);
+                       break;
                }
        }
        kfree(devlist, M_TEMP);
@@ -245,7 +305,8 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context,
 static int
 acpi_pci_probe(device_t dev)
 {
-       if (pcib_get_bus(device_get_parent(dev)) < 0)
+
+       if (pcib_get_bus(dev) < 0)
                return (ENXIO);
        if (acpi_get_handle(dev) == NULL)
                return (ENXIO);
@@ -260,15 +321,15 @@ acpi_pci_attach(device_t dev)
 
        /*
         * Since there can be multiple independantly numbered PCI
-        * busses on some large alpha systems, we can't use the unit
-        * number to decide what bus we are probing. We ask the parent 
-        * pcib what our bus number is.
+        * busses on systems with multiple PCI domains, we can't use
+        * the unit number to decide which bus we are probing. We ask
+        * the parent pcib what our domain and bus numbers are.
         */
-       busno = pcib_get_bus(device_get_parent(dev));
+       busno = pcib_get_bus(dev);
+       domain = pcib_get_domain(dev);
        if (bootverbose)
-               device_printf(dev, "physical bus=%d\n", busno);
-
-       domain = pcib_get_domain(device_get_parent(dev));
+              device_printf(dev, "domain=%d, physical bus=%d\n",
+                    domain, busno);
 
        /*
         * First, PCI devices are added as in the normal PCI bus driver.
@@ -286,3 +347,79 @@ acpi_pci_attach(device_t dev)
 
        return (bus_generic_attach(dev));
 }
+
+int
+acpi_pci_suspend(device_t dev)
+{
+        int dstate, error, i, numdevs;
+        device_t acpi_dev, child, *devlist;
+        struct pci_devinfo *dinfo;
+        /*
+         * Save the PCI configuration space for each child and set the
+         * device in the appropriate power state for this sleep state.
+         */
+        acpi_dev = NULL;
+       acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
+        device_get_children(dev, &devlist, &numdevs);
+        for (i = 0; i < numdevs; i++) {
+                child = devlist[i];
+                dinfo = (struct pci_devinfo *) device_get_ivars(child);
+                pci_cfg_save(child, dinfo, 0);
+        }
+        /* Suspend devices before potentially powering them down. */
+        error = bus_generic_suspend(dev);
+        if (error) {
+                kfree(devlist, M_TEMP);
+                return (error);
+        }
+        /*
+         * Always set the device to D3.  If ACPI suggests a different
+         * power state, use it instead.  If ACPI is not present, the
+         * firmware is responsible for managing device power.  Skip
+         * children who aren't attached since they are powered down
+         * separately.  Only manage type 0 devices for now.
+         */
+        for (i = 0; acpi_dev && i < numdevs; i++) {
+                child = devlist[i];
+                dinfo = (struct pci_devinfo *) device_get_ivars(child);
+                if (device_is_attached(child) && dinfo->cfg.hdrtype == 0) {
+                        dstate = PCI_POWERSTATE_D3;
+                        ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate);
+                        pci_set_powerstate(child, dstate);
+                }
+        }
+        kfree(devlist, M_TEMP);
+        return (0);
+}
+
+int
+acpi_pci_resume(device_t dev)
+{
+        int i, numdevs;
+        device_t acpi_dev, child, *devlist;
+        struct pci_devinfo *dinfo;
+        /*
+         * Set each child to D0 and restore its PCI configuration space.
+         */
+        acpi_dev = NULL;
+       acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
+        device_get_children(dev, &devlist, &numdevs);
+        for (i = 0; i < numdevs; i++) {
+                /*
+                 * Notify ACPI we're going to D0 but ignore the result.  If
+                 * ACPI is not present, the firmware is responsible for
+                 * managing device power.  Only manage type 0 devices for now.
+                 */
+                child = devlist[i];
+                dinfo = (struct pci_devinfo *) device_get_ivars(child);
+                if (acpi_dev && device_is_attached(child) &&
+                    dinfo->cfg.hdrtype == 0) {
+                       ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL);
+                        pci_set_powerstate(child, PCI_POWERSTATE_D0);
+                }
+                /* Now the device is powered up, restore its config space. */
+                pci_cfg_restore(child, dinfo);
+        }
+        kfree(devlist, M_TEMP);
+        return (bus_generic_resume(dev));
+}
index 42e0ca8..b40afdc 100644 (file)
@@ -1,5 +1,5 @@
-/*-
- * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+/*
+ * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.kfreebsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.16 2004/06/14 18:54:14 jhb Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pci_link.c,v 1.7 2006/12/22 23:26:14 swildner Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.56.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $");
  */
 
+#define MPASS(ex)               MPASS4(ex, #ex, __FILE__, __LINE__)
+#define MPASS4(ex, what, file, line)                                    \
+        KASSERT((ex), ("Assertion %s failed at %s:%d", what, file, line))
+
+#include <sys/cdefs.h>
+
 #include "opt_acpi.h"
 #include <sys/param.h>
-#include <sys/kernel.h>
 #include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
 
 #include "acpi.h"
-#include "accommon.h"
 #include <dev/acpica5/acpivar.h>
 #include <dev/acpica5/acpi_pcibvar.h>
 
+#include <bus/pci/i386/pci_cfgreg.h>
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+#include "pcib_if.h"
+
 /* Hooks for the ACPI CA debugging infrastructure. */
 #define _COMPONENT     ACPI_BUS
 ACPI_MODULE_NAME("PCI_LINK")
 
-#define MAX_POSSIBLE_INTERRUPTS        16
-#define MAX_ISA_INTERRUPTS     16
-#define MAX_ACPI_INTERRUPTS    255
-
-struct acpi_pci_link_entry {
-       TAILQ_ENTRY(acpi_pci_link_entry) links;
-       ACPI_HANDLE     handle;
-       UINT8           current_irq;
-       UINT8           initial_irq;
-       ACPI_RESOURCE   possible_resources;
-       UINT8           number_of_interrupts;
-       UINT8           interrupts[MAX_POSSIBLE_INTERRUPTS];
-       UINT8           sorted_irq[MAX_POSSIBLE_INTERRUPTS];
-       int             references;
-       int             priority;
+ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
+
+#define NUM_ISA_INTERRUPTS     16
+#define NUM_ACPI_INTERRUPTS    256
+
+/*
+ * An ACPI PCI link device may contain multiple links.  Each link has its
+ * own ACPI resource.  _PRT entries specify which link is being used via
+ * the Source Index.
+ *
+ * XXX: A note about Source Indices and DPFs:  Currently we assume that
+ * the DPF start and end tags are not counted towards the index that
+ * Source Index corresponds to.  Also, we assume that when DPFs are in use
+ * they various sets overlap in terms of Indices.  Here's an example
+ * resource list indicating these assumptions:
+ *
+ * Resource            Index
+ * --------            -----
+ * I/O Port            0
+ * Start DPF           -
+ * IRQ                 1
+ * MemIO               2
+ * Start DPF           -
+ * IRQ                 1
+ * MemIO               2
+ * End DPF             -
+ * DMA Channel         3
+ *
+ * The XXX is because I'm not sure if this is a valid assumption to make.
+ */
+
+/* States during DPF processing. */
+#define        DPF_OUTSIDE     0
+#define        DPF_FIRST       1
+#define        DPF_IGNORE      2
+
+struct link;
+
+struct acpi_pci_link_softc {
+       int     pl_num_links;
+       int     pl_crs_bad;
+       struct link *pl_links;
+       device_t pl_dev;
 };
 
-TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
-static struct acpi_pci_link_entries acpi_pci_link_entries;
+struct link {
+       struct acpi_pci_link_softc *l_sc;
+       uint8_t l_bios_irq;
+       uint8_t l_irq;
+       uint8_t l_initial_irq;
+       int     l_res_index;
+       int     l_num_irqs;
+       int     *l_irqs;
+       int     l_references;
+       int     l_routed:1;
+       int     l_isa_irq:1;
+       ACPI_RESOURCE l_prs_template;
+};
 
-struct acpi_prt_entry {
-       TAILQ_ENTRY(acpi_prt_entry) links;
-       device_t        pcidev;
-       int             busno;
-       ACPI_PCI_ROUTING_TABLE prt;
-       struct acpi_pci_link_entry *pci_link;
+struct link_count_request {
+       int     in_dpf;
+       int     count;
 };
 
-TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
-static struct acpi_prt_entries acpi_prt_entries;
+struct link_res_request {
+       struct acpi_pci_link_softc *sc;
+       int     in_dpf;
+       int     res_index;
+       int     link_index;
+};
+
+MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures");
 
-static int     irq_penalty[MAX_ACPI_INTERRUPTS];
+static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
+static int pci_link_bios_isa_irqs;
 
-#define ACPI_STA_PRESENT       0x00000001
-#define ACPI_STA_ENABLE                0x00000002
-#define ACPI_STA_SHOWINUI      0x00000004
-#define ACPI_STA_FUNCTIONAL    0x00000008
+static char *pci_link_ids[] = { "PNP0C0F", NULL };
 
 /*
- * PCI link object management
+ * Fetch the short name associated with an ACPI handle and save it in the
+ * passed in buffer.
  */
-
-static void
-acpi_pci_link_dump_polarity(UINT32 Polarity)
+static ACPI_STATUS
+acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
 {
+       ACPI_BUFFER buf;
 
-       switch (Polarity) {
-       case ACPI_ACTIVE_HIGH:
-               kprintf("high,");
-               break;
-       case ACPI_ACTIVE_LOW:
-               kprintf("low,");
-               break;
-       default:
-               kprintf("unknown,");
-               break;
-       }
+       buf.Length = buflen;
+       buf.Pointer = buffer;
+       return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
 }
 
-static void
-acpi_pci_link_dump_trigger(UINT32 Triggering)
+static int
+acpi_pci_link_probe(device_t dev)
 {
+       char descr[28], name[12];
 
-       switch (Triggering) {
-       case ACPI_EDGE_SENSITIVE:
-               kprintf("edge,");
-               break;
-       case ACPI_LEVEL_SENSITIVE:
-               kprintf("level,");
-               break;
-       default:
-               kprintf("unknown,");
-               break;
-       }
+       /*
+        * We explicitly do not check _STA since not all systems set it to
+        * sensible values.
+        */
+       if (acpi_disabled("pci_link") ||
+           ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids) == NULL)
+               return (ENXIO);
+
+       if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
+           sizeof(name)))) {
+               ksnprintf(descr, sizeof(descr), "ACPI PCI Link %s", name);
+               device_set_desc_copy(dev, descr);
+       } else
+               device_set_desc(dev, "ACPI PCI Link");
+       device_quiet(dev);
+       return (0);
 }
 
-static void
-acpi_pci_link_dump_sharemode(UINT32 SharedExclusive)
+static ACPI_STATUS
+acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
 {
-
-       switch (SharedExclusive) {
-       case ACPI_EXCLUSIVE:
-               kprintf("exclusive");
+       struct link_count_request *req;
+
+       req = (struct link_count_request *)context;
+       switch (res->Type) {
+       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+               switch (req->in_dpf) {
+               case DPF_OUTSIDE:
+                       /* We've started the first DPF. */
+                       req->in_dpf = DPF_FIRST;
+                       break;
+               case DPF_FIRST:
+                       /* We've started the second DPF. */
+                       req->in_dpf = DPF_IGNORE;
+                       break;
+               }
                break;
-       case ACPI_SHARED:
-               kprintf("sharable");
+       case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+               /* We are finished with DPF parsing. */
+               KASSERT(req->in_dpf != DPF_OUTSIDE,
+                   ("%s: end dpf when not parsing a dpf", __func__));
+               req->in_dpf = DPF_OUTSIDE;
                break;
-       default:
-               kprintf("unknown");
-               break;
-       }
-}
-
-static void
-acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
-{
-       UINT8                   i;
-       ACPI_RESOURCE_IRQ       *Irq;
-       ACPI_RESOURCE_EXTENDED_IRQ      *ExtIrq;
-
-       if (entry == NULL || entry->pci_link == NULL)
-               return;
-
-       kprintf("%s irq %3d: ", acpi_name(entry->pci_link->handle),
-           entry->pci_link->current_irq);
-
-       kprintf("[");
-       for (i = 0; i < entry->pci_link->number_of_interrupts; i++)
-               kprintf("%3d", entry->pci_link->interrupts[i]);
-       kprintf("] ");
-
-       switch (entry->pci_link->possible_resources.Type) {
        case ACPI_RESOURCE_TYPE_IRQ:
-               Irq = &entry->pci_link->possible_resources.Data.Irq;
-               acpi_pci_link_dump_polarity(Irq->Polarity);
-               acpi_pci_link_dump_trigger(Irq->Triggering);
-               acpi_pci_link_dump_sharemode(Irq->Sharable);
-               break;
        case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
-               ExtIrq = &entry->pci_link->possible_resources.Data.ExtendedIrq;
-               acpi_pci_link_dump_polarity(ExtIrq->Polarity);
-               acpi_pci_link_dump_trigger(ExtIrq->Triggering);
-               acpi_pci_link_dump_sharemode(ExtIrq->Sharable);
-               break;
+               /*
+                * Don't count resources if we are in a DPF set that we are
+                * ignoring.
+                */
+               if (req->in_dpf != DPF_IGNORE)
+                       req->count++;
        }
-
-       kprintf(" %d.%d.%d\n", entry->busno,
-           (int)((entry->prt.Address & 0xffff0000) >> 16),
-           (int)entry->prt.Pin);
+       return (AE_OK);
 }
 
 static ACPI_STATUS
-acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
+link_add_crs(ACPI_RESOURCE *res, void *context)
 {
-       ACPI_DEVICE_INFO        *devinfo;
-       ACPI_BUFFER             buf;
-       ACPI_STATUS             error;
-
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
-       if (handle == NULL || sta == NULL) {
-               kprintf("invalid argument\n");
-               return_ACPI_STATUS (AE_BAD_PARAMETER);
-       }
-
-       buf.Pointer = NULL;
-       buf.Length = ACPI_ALLOCATE_BUFFER;
-       error = AcpiGetObjectInfo(handle, &buf);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get object info %s - %s\n",
-                   acpi_name(handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
-       }
+       struct link_res_request *req;
+       struct link *link;
+
+       ACPI_SERIAL_ASSERT(pci_link);
+       req = (struct link_res_request *)context;
+       switch (res->Type) {
+       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+               switch (req->in_dpf) {
+               case DPF_OUTSIDE:
+                       /* We've started the first DPF. */
+                       req->in_dpf = DPF_FIRST;
+                       break;
+               case DPF_FIRST:
+                       /* We've started the second DPF. */
+                       panic(
+               "%s: Multiple dependent functions within a current resource",
+                           __func__);
+                       break;
+               }
+               break;
+       case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+               /* We are finished with DPF parsing. */
+               KASSERT(req->in_dpf != DPF_OUTSIDE,
+                   ("%s: end dpf when not parsing a dpf", __func__));
+               req->in_dpf = DPF_OUTSIDE;
+               break;
+       case ACPI_RESOURCE_TYPE_IRQ:
+       case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+               KASSERT(req->link_index < req->sc->pl_num_links,
+                   ("%s: array boundary violation", __func__));
+               link = &req->sc->pl_links[req->link_index];
+               link->l_res_index = req->res_index;
+               req->link_index++;
+               req->res_index++;
 
-       devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
-       if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
-           strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) {
-               kprintf("invalid hardware ID - %s\n", acpi_name(handle));
-               AcpiOsFree(buf.Pointer);
-               return_ACPI_STATUS (AE_TYPE);
-       }
+               /*
+                * Only use the current value if there's one IRQ.  Some
+                * systems return multiple IRQs (which is nonsense for _CRS)
+                * when the link hasn't been programmed.
+                */
+               if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
+                       if (res->Data.Irq.InterruptCount == 1)
+                               link->l_irq = res->Data.Irq.Interrupts[0];
+               } else if (res->Data.ExtendedIrq.InterruptCount == 1)
+                       link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
 
-       if ((devinfo->Valid & ACPI_VALID_STA) != 0) {
-               *sta = devinfo->CurrentStatus;
-       } else {
-               kprintf("invalid status - %s\n", acpi_name(handle));
-               *sta = 0;
+               /*
+                * An IRQ of zero means that the link isn't routed.
+                */
+               if (link->l_irq == 0)
+                       link->l_irq = PCI_INVALID_IRQ;
+               break;
+       default:
+               req->res_index++;
        }
-
-       AcpiOsFree(buf.Pointer);
-       return_ACPI_STATUS (AE_OK);
+       return (AE_OK);
 }
 
+/*
+ * Populate the set of possible IRQs for each device.
+ */
 static ACPI_STATUS
-acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
-    UINT8 *number_of_interrupts, UINT8 interrupts[])
+link_add_prs(ACPI_RESOURCE *res, void *context)
 {
-       UINT8                   count;
-       UINT8                   i;
-       UINT32                  InterruptCount;
-       UINT32                  *Interrupts32;
-       UINT8                   *Interrupts8;
-
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
-       if (resources == NULL || number_of_interrupts == NULL) {
-               kprintf("invalid argument\n");
-               return_ACPI_STATUS (AE_BAD_PARAMETER);
-       }
-
-       *number_of_interrupts = 0;
-       InterruptCount = 0;
-       Interrupts8 = NULL;
-       Interrupts32 = NULL;
-
-       if (resources->Type == ACPI_RESOURCE_TYPE_START_DEPENDENT)
-               resources = ACPI_NEXT_RESOURCE(resources);
-
-       if (resources->Type != ACPI_RESOURCE_TYPE_IRQ &&
-           resources->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
-               kprintf("Resource is not an IRQ entry - %d\n", resources->Type);
-               return_ACPI_STATUS (AE_TYPE);
-       }
-
-       switch (resources->Type) {
-       case ACPI_RESOURCE_TYPE_IRQ:
-               InterruptCount = resources->Data.Irq.InterruptCount;
-               Interrupts8 = resources->Data.Irq.Interrupts;
+       struct link_res_request *req;
+       struct link *link;
+       UINT8 *irqs = NULL;
+       UINT32 *ext_irqs = NULL;
+       int i, is_ext_irq = 1;
+
+       ACPI_SERIAL_ASSERT(pci_link);
+       req = (struct link_res_request *)context;
+       switch (res->Type) {
+       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+               switch (req->in_dpf) {
+               case DPF_OUTSIDE:
+                       /* We've started the first DPF. */
+                       req->in_dpf = DPF_FIRST;
+                       break;
+               case DPF_FIRST:
+                       /* We've started the second DPF. */
+                       req->in_dpf = DPF_IGNORE;
+                       break;
+               }
                break;
-       case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
-                InterruptCount =
-                   resources->Data.ExtendedIrq.InterruptCount;
-                Interrupts32 = resources->Data.ExtendedIrq.Interrupts;
+       case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+               /* We are finished with DPF parsing. */
+               KASSERT(req->in_dpf != DPF_OUTSIDE,
+                   ("%s: end dpf when not parsing a dpf", __func__));
+               req->in_dpf = DPF_OUTSIDE;
                break;
-       }
-       
-       if (InterruptCount == 0) {
-               kprintf("Blank IRQ resource\n");
-               return_ACPI_STATUS (AE_NULL_ENTRY);
-       }
-
-       count = 0;
-       for (i = 0; i < InterruptCount; i++) {
-               UINT32 intr;
-
-               if (i >= MAX_POSSIBLE_INTERRUPTS) {
-                       kprintf("too many IRQs (%d)\n", i);
+       case ACPI_RESOURCE_TYPE_IRQ:
+               is_ext_irq = 0;
+               /* fall through */
+       case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+               /*
+                * Don't parse resources if we are in a DPF set that we are
+                * ignoring.
+                */
+               if (req->in_dpf == DPF_IGNORE)
                        break;
+
+               KASSERT(req->link_index < req->sc->pl_num_links,
+                   ("%s: array boundary violation", __func__));
+               link = &req->sc->pl_links[req->link_index];
+               if (link->l_res_index == -1) {
+                       KASSERT(req->sc->pl_crs_bad,
+                           ("res_index should be set"));
+                       link->l_res_index = req->res_index;
                }
+               req->link_index++;
+               req->res_index++;
 
-               KKASSERT(Interrupts8 != NULL || Interrupts32 != NULL);
-               if (Interrupts8 != NULL)
-                       intr = Interrupts8[i];
-               else
-                       intr = Interrupts32[i];
+               /*
+                * Stash a copy of the resource for later use when doing
+                * _SRS.
+                */
+               bcopy(res, &link->l_prs_template, sizeof(ACPI_RESOURCE));
+               if (is_ext_irq) {
+                       link->l_num_irqs =
+                           res->Data.ExtendedIrq.InterruptCount;
+                       ext_irqs = res->Data.ExtendedIrq.Interrupts;
+               } else {
+                       link->l_num_irqs = res->Data.Irq.InterruptCount;
+                       irqs = res->Data.Irq.Interrupts;
+               }
+               if (link->l_num_irqs == 0)
+                       break;
 
-               if (intr == 0) {
-                       kprintf("invalid IRQ %d\n", intr);
-                       continue;
+               /*
+                * Save a list of the valid IRQs.  Also, if all of the
+                * valid IRQs are ISA IRQs, then mark this link as
+                * routed via an ISA interrupt.
+                */
+#ifdef APIC_IO
+               link->l_isa_irq = FALSE;
+#else
+               link->l_isa_irq = TRUE;
+#endif
+
+               link->l_irqs = kmalloc(sizeof(int) * link->l_num_irqs,
+                   M_PCI_LINK, M_WAITOK | M_ZERO);
+               for (i = 0; i < link->l_num_irqs; i++) {
+                       if (is_ext_irq) {
+                               link->l_irqs[i] = ext_irqs[i];
+                               if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
+                                       link->l_isa_irq = FALSE;
+                       } else {
+                               link->l_irqs[i] = irqs[i];
+                               if (irqs[i] >= NUM_ISA_INTERRUPTS)
+                                       link->l_isa_irq = FALSE;
+                       }
                }
-               interrupts[count] = intr;
-               count++;
+               break;
+       default:
+               if (req->in_dpf == DPF_IGNORE)
+                       break;
+               if (req->sc->pl_crs_bad)
+                       device_printf(req->sc->pl_dev,
+                   "Warning: possible resource %d will be lost during _SRS\n",
+                           req->res_index);
+               req->res_index++;
        }
-       *number_of_interrupts = count;
-
-       return_ACPI_STATUS (AE_OK);
+       return (AE_OK);
 }
 
-static ACPI_STATUS
-acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
+static int
+link_valid_irq(struct link *link, int irq)
 {
-       ACPI_STATUS             error;
-       ACPI_BUFFER             buf;
-       ACPI_RESOURCE           *resources;
-       UINT8                   number_of_interrupts;
-       UINT8                   interrupts[MAX_POSSIBLE_INTERRUPTS];
-
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
-       if (link == NULL || irq == NULL) {
-               kprintf("invalid argument\n");
-               return_ACPI_STATUS (AE_BAD_PARAMETER);
-       }
+       int i;
 
-       *irq = 0;
-       buf.Pointer = NULL;
-       buf.Length = ACPI_ALLOCATE_BUFFER;
-       error = AcpiGetCurrentResources(link->handle, &buf);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get PCI interrupt link device _CRS %s - %s\n",
-                   acpi_name(link->handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
-       }
-       if (buf.Pointer == NULL) {
-               kprintf("couldn't allocate memory - %s\n",
-                   acpi_name(link->handle));
-               return_ACPI_STATUS (AE_NO_MEMORY);
-       }
+       ACPI_SERIAL_ASSERT(pci_link);
 
-       resources = (ACPI_RESOURCE *) buf.Pointer;
-       number_of_interrupts = 0;
-       bzero(interrupts, sizeof(interrupts));
-       error = acpi_pci_link_get_irq_resources(resources,
-                   &number_of_interrupts, interrupts);
-       AcpiOsFree(buf.Pointer);
-
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get current IRQ from "
-                   "interrupt link %s - %s\n",
-                   acpi_name(link->handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
-       }
+       /* Invalid interrupts are never valid. */
+       if (!PCI_INTERRUPT_VALID(irq))
+               return (FALSE);
 
-       if (number_of_interrupts == 0) {
-               kprintf("PCI interrupt link device _CRS data is corrupted - "
-                   "%s\n", acpi_name(link->handle));
-               return_ACPI_STATUS (AE_NULL_ENTRY);
-       }
+       /* Any interrupt in the list of possible interrupts is valid. */
+       for (i = 0; i < link->l_num_irqs; i++)
+               if (link->l_irqs[i] == irq)
+                        return (TRUE);
 
-       *irq = interrupts[0];
+       /*
+        * For links routed via an ISA interrupt, if the SCI is routed via
+        * an ISA interrupt, the SCI is always treated as a valid IRQ.
+        */
+       if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
+           irq < NUM_ISA_INTERRUPTS)
+               return (TRUE);
 
-       return_ACPI_STATUS (AE_OK);
+       /* If the interrupt wasn't found in the list it is not valid. */
+       return (FALSE);
 }
 
-static ACPI_STATUS
-acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
+static void
+acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag)
 {
-       ACPI_STATUS             error;
-       ACPI_BUFFER             buf;
-       ACPI_RESOURCE           *resources;
-       struct acpi_pci_link_entry *link;
-
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
-       entry->pci_link = NULL;
-       TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
-               if (link->handle == handle) {
-                       entry->pci_link = link;
-                       link->references++;
-                       return_ACPI_STATUS (AE_OK);
-               }
+       struct link *link;
+       char buf[16];
+       int i, j;
+
+       ACPI_SERIAL_ASSERT(pci_link);
+       if (header) {
+               ksnprintf(buf, sizeof(buf), "%s:",
+                   device_get_nameunit(sc->pl_dev));
+               kprintf("%-16.16s  Index  IRQ  Rtd  Ref  IRQs\n", buf);
        }
-
-       link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
-       if (link == NULL) {
-               kprintf("couldn't allocate memory - %s\n", acpi_name(handle));
-               return_ACPI_STATUS (AE_NO_MEMORY);
-       }
-
-       buf.Pointer = NULL;
-       buf.Length = ACPI_ALLOCATE_BUFFER;
-
-       bzero(link, sizeof(struct acpi_pci_link_entry));
-
-       link->handle = handle;
-
-       error = acpi_pci_link_get_current_irq(link, &link->current_irq);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get current IRQ from "
-                   "interrupt link %s - %s\n",
-                   acpi_name(handle), AcpiFormatException(error));
-       }
-
-       link->initial_irq = link->current_irq;
-
-       error = AcpiGetPossibleResources(handle, &buf);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get interrupt link device _PRS "
-                   "data %s - %s\n",
-                   acpi_name(handle), AcpiFormatException(error));
-               goto out;
-       }
-       if (buf.Pointer == NULL) {
-               kprintf("_PRS nuffer is empty - %s\n", acpi_name(handle));
-               error = AE_NO_MEMORY;
-               goto out;
-       }
-
-       resources = (ACPI_RESOURCE *) buf.Pointer;
-       bcopy(resources, &link->possible_resources,
-           sizeof(link->possible_resources));
-
-       error = acpi_pci_link_get_irq_resources(resources,
-           &link->number_of_interrupts, link->interrupts);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get possible IRQs from "
-                   "interrupt link %s - %s\n",
-                   acpi_name(handle), AcpiFormatException(error));
-               goto out;
-       }
-
-       if (link->number_of_interrupts == 0) {
-               kprintf("interrupt link device _PRS data is corrupted - %s\n",
-                   acpi_name(handle));
-               error = AE_NULL_ENTRY;
-               goto out;
+       for (i = 0; i < sc->pl_num_links; i++) {
+               link = &sc->pl_links[i];
+               kprintf("  %-14.14s  %5d  %3d   %c   %3d ", i == 0 ? tag : "", i,
+                   link->l_irq, link->l_routed ? 'Y' : 'N',
+                   link->l_references);
+               if (link->l_num_irqs == 0)
+                       kprintf(" none");
+               else for (j = 0; j < link->l_num_irqs; j++)
+                       kprintf(" %d", link->l_irqs[j]);
+               kprintf("\n");
        }
-
-       link->references++;
-
-       TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
-       entry->pci_link = link;
-
-       error = AE_OK;
-out:
-       if (buf.Pointer != NULL)
-               AcpiOsFree(buf.Pointer);
-       if (error != AE_OK && link != NULL)
-               AcpiOsFree(link);
-
-       return_ACPI_STATUS (error);
 }
 
-static ACPI_STATUS
-acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
+static int
+acpi_pci_link_attach(device_t dev)
 {
-       ACPI_HANDLE             handle;
-       ACPI_STATUS             error;
-       UINT32                  sta;
-       struct acpi_prt_entry   *entry;
+       struct acpi_pci_link_softc *sc;
+       struct link_count_request creq;
+       struct link_res_request rreq;
+       ACPI_STATUS status;
+       int i;
 
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
-       if (prt == NULL || prt->Source == NULL || prt->Source[0] == '\0') {
-               kprintf("couldn't handle this routing table - hardwired\n");
-               return_ACPI_STATUS (AE_BAD_PARAMETER);
-       }
+       sc = device_get_softc(dev);
+       sc->pl_dev = dev;
+       ACPI_SERIAL_BEGIN(pci_link);
 
-       error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get handle - %s\n",
-                   AcpiFormatException(error));
-               return_ACPI_STATUS (error);
+       /*
+        * Count the number of current resources so we know how big of
+        * a link array to allocate.  On some systems, _CRS is broken,
+        * so for those systems try to derive the count from _PRS instead.
+        */
+       creq.in_dpf = DPF_OUTSIDE;
+       creq.count = 0;
+       status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
+           acpi_count_irq_resources, &creq);
+       sc->pl_crs_bad = ACPI_FAILURE(status);
+       if (sc->pl_crs_bad) {
+               creq.in_dpf = DPF_OUTSIDE;
+               creq.count = 0;
+               status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
+                   acpi_count_irq_resources, &creq);
+               if (ACPI_FAILURE(status)) {
+                       device_printf(dev,
+                           "Unable to parse _CRS or _PRS: %s\n",
+                           AcpiFormatException(status));
+                       ACPI_SERIAL_END(pci_link);
+                       return (ENXIO);
+               }
        }
-
-       error = acpi_pci_link_get_object_status(handle, &sta);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get object status %s - %s\n",
-                   acpi_name(handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
+       sc->pl_num_links = creq.count;
+       if (creq.count == 0) {
+               ACPI_SERIAL_END(pci_link);
+               return (0);
        }
-
-       if ((sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL)) == 0) {
-               kprintf("interrupt link is not functional - %s\n",
-                   acpi_name(handle));
-               return_ACPI_STATUS (AE_ERROR);
+       sc->pl_links = kmalloc(sizeof(struct link) * sc->pl_num_links,
+           M_PCI_LINK, M_WAITOK | M_ZERO);
+
+       /* Initialize the child links. */
+       for (i = 0; i < sc->pl_num_links; i++) {
+               sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
+               sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
+               sc->pl_links[i].l_sc = sc;
+               sc->pl_links[i].l_isa_irq = FALSE;
+               sc->pl_links[i].l_res_index = -1;
        }
 
-       TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
-               if (entry->busno == busno &&
-                   entry->prt.Address == prt->Address &&
-                   entry->prt.Pin == prt->Pin) {
-                       kprintf("interrupt link entry already exists - %s\n",
-                           acpi_name(handle));
-                       return_ACPI_STATUS (AE_ALREADY_EXISTS);
+       /* Try to read the current settings from _CRS if it is valid. */
+       if (!sc->pl_crs_bad) {
+               rreq.in_dpf = DPF_OUTSIDE;
+               rreq.link_index = 0;
+               rreq.res_index = 0;
+               rreq.sc = sc;
+               status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
+                   link_add_crs, &rreq);
+               if (ACPI_FAILURE(status)) {
+                       device_printf(dev, "Unable to parse _CRS: %s\n",
+                           AcpiFormatException(status));
+                       goto fail;
                }
        }
 
-       entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
-       if (entry == NULL) {
-               kprintf("couldn't allocate memory - %s\n", acpi_name(handle));
-               return_ACPI_STATUS (AE_NO_MEMORY);
-       }
-       bzero(entry, sizeof(struct acpi_prt_entry));
-
-       entry->pcidev = pcidev;
-       entry->busno = busno;
-       bcopy(prt, &entry->prt, sizeof(entry->prt));
-
-       error = acpi_pci_link_add_link(handle, entry);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't add _PRT entry to link %s - %s\n",
-                   acpi_name(handle), AcpiFormatException(error));
-               goto out;
+       /*
+        * Try to read the possible settings from _PRS.  Note that if the
+        * _CRS is toast, we depend on having a working _PRS.  However, if
+        * _CRS works, then it is ok for _PRS to be missing.
+        */
+       rreq.in_dpf = DPF_OUTSIDE;
+       rreq.link_index = 0;
+       rreq.res_index = 0;
+       rreq.sc = sc;
+       status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
+           link_add_prs, &rreq);
+       if (ACPI_FAILURE(status) &&
+           (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
+               device_printf(dev, "Unable to parse _PRS: %s\n",
+                   AcpiFormatException(status));
+               goto fail;
        }
+       if (bootverbose)
+               acpi_pci_link_dump(sc, 1, "Initial Probe");
+
+       /* Verify initial IRQs if we have _PRS. */
+       if (status != AE_NOT_FOUND)
+               for (i = 0; i < sc->pl_num_links; i++)
+                       if (!link_valid_irq(&sc->pl_links[i],
+                           sc->pl_links[i].l_irq))
+                               sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
+       if (bootverbose)
+               acpi_pci_link_dump(sc, 0, "Validation");
+
+       /* Save initial IRQs. */
+       for (i = 0; i < sc->pl_num_links; i++)
+               sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
 
-       TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
-       error = AE_OK;
-
-out:
-       if (error != AE_OK && entry != NULL)
-               AcpiOsFree(entry);
-
-       return_ACPI_STATUS (error);
+       /*
+        * Try to disable this link.  If successful, set the current IRQ to
+        * zero and flags to indicate this link is not routed.  If we can't
+        * run _DIS (i.e., the method doesn't exist), assume the initial
+        * IRQ was routed by the BIOS.
+        */
+       if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
+           NULL)))
+               for (i = 0; i < sc->pl_num_links; i++)
+                       sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
+       else
+               for (i = 0; i < sc->pl_num_links; i++)
+                       if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
+                               sc->pl_links[i].l_routed = TRUE;
+       if (bootverbose)
+               acpi_pci_link_dump(sc, 0, "After Disable");
+       ACPI_SERIAL_END(pci_link);
+       return (0);
+fail:
+       ACPI_SERIAL_END(pci_link);
+       for (i = 0; i < sc->pl_num_links; i++)
+               if (sc->pl_links[i].l_irqs != NULL)
+                       kfree(sc->pl_links[i].l_irqs, M_PCI_LINK);
+       kfree(sc->pl_links, M_PCI_LINK);
+       return (ENXIO);
 }
 
-static int
-acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
+/* XXX: Note that this is identical to pci_pir_search_irq(). */
+static uint8_t
+acpi_pci_link_search_irq(int bus, int device, int pin)
 {
-       UINT8                   i;
-
-       if (irq == 0)
-               return (0);
+       uint32_t value;
+       uint8_t func, maxfunc;
+
+       /* See if we have a valid device at function 0. */
+       value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
+       if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+               return (PCI_INVALID_IRQ);
+       if (value & PCIM_MFDEV)
+               maxfunc = PCI_FUNCMAX;
+       else
+               maxfunc = 0;
+
+       /* Scan all possible functions at this device. */
+       for (func = 0; func <= maxfunc; func++) {
+               value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
+               if (value == 0xffffffff)
+                       continue;
+               value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
 
-       for (i = 0; i < link->number_of_interrupts; i++) {
-               if (link->interrupts[i] == irq)
-                       return (1);
+               /*
+                * See if it uses the pin in question.  Note that the passed
+                * in pin uses 0 for A, .. 3 for D whereas the intpin
+                * register uses 0 for no interrupt, 1 for A, .. 4 for D.
+                */
+               if (value != pin + 1)
+                       continue;
+               value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
+               if (bootverbose)
+                       kprintf(
+               "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
+                           bus, device, pin + 'A', func, value);
+               if (value != PCI_INVALID_IRQ)
+                       return (value);
        }
-
-       /* allow initial IRQ as valid one. */
-       if (link->initial_irq == irq)
-               return (1);
-
-       return (0);
+       return (PCI_INVALID_IRQ);
 }
 
-static ACPI_STATUS
-acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
+/*
+ * Find the link structure that corresponds to the resource index passed in
+ * via 'source_index'.
+ */
+static struct link *
+acpi_pci_link_lookup(device_t dev, int source_index)
 {
-       ACPI_STATUS             error;
-       ACPI_RESOURCE           resbuf;
-       ACPI_BUFFER             crsbuf;
-       UINT32                  sta;
-
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
-       if (!acpi_pci_link_is_valid_irq(link, irq)) {
-               kprintf("couldn't set invalid IRQ %d - %s\n", irq,
-                   acpi_name(link->handle));
-               return_ACPI_STATUS (AE_BAD_PARAMETER);
-       }
-
-       error = acpi_pci_link_get_current_irq(link, &link->current_irq);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get current IRQ from "
-                   "interrupt link %s - %s\n",
-                   acpi_name(link->handle), AcpiFormatException(error));
-       }
-
-       if (link->current_irq == irq)
-               return_ACPI_STATUS (AE_OK);
-
-       bzero(&resbuf, sizeof(resbuf));
-       crsbuf.Pointer = NULL;
-
-       switch (link->possible_resources.Type) {
-       case ACPI_RESOURCE_TYPE_IRQ:
-               resbuf.Type = ACPI_RESOURCE_TYPE_IRQ;
-               resbuf.Length = sizeof(ACPI_RESOURCE_IRQ);
-
-               /* structure copy other fields */
-               resbuf.Data.Irq = link->possible_resources.Data.Irq;
-               resbuf.Data.Irq.InterruptCount = 1;
-               resbuf.Data.Irq.Interrupts[0] = irq;
-               break;
-       case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
-               resbuf.Type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
-               resbuf.Length = sizeof(ACPI_RESOURCE_EXTENDED_IRQ);
-
-               /* structure copy other fields */
-               resbuf.Data.ExtendedIrq =
-                   link->possible_resources.Data.ExtendedIrq;
-               resbuf.Data.ExtendedIrq.InterruptCount = 1;
-               resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
-               break;
-       default:
-               kprintf("Resource is not an IRQ entry %s - %d\n",
-                   acpi_name(link->handle), link->possible_resources.Type);
-               return_ACPI_STATUS (AE_TYPE);
-       }
-
-       error = acpi_AppendBufferResource(&crsbuf, &resbuf);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't setup buffer by "
-                   "acpi_AppendBufferResource - %s\n",
-                   acpi_name(link->handle));
-               return_ACPI_STATUS (error);
-       }
-       if (crsbuf.Pointer == NULL) {
-               kprintf("appended buffer for %s is corrupted\n",
-                   acpi_name(link->handle));
-               return_ACPI_STATUS (AE_NO_MEMORY);
-       }
-
-       error = AcpiSetCurrentResources(link->handle, &crsbuf);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't set link device _SRS %s - %s\n",
-                   acpi_name(link->handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
-       }
+       struct acpi_pci_link_softc *sc;
+       int i;
+
+       ACPI_SERIAL_ASSERT(pci_link);
+       sc = device_get_softc(dev);
+       for (i = 0; i < sc->pl_num_links; i++)
+               if (sc->pl_links[i].l_res_index == source_index)
+                       return (&sc->pl_links[i]);
+       return (NULL);
+}
 
-       AcpiOsFree(crsbuf.Pointer);
-       link->current_irq = 0;
+void
+acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
+    int pin)
+{
+       struct link *link;
+       uint8_t bios_irq;
+       uintptr_t bus;
 
-       error = acpi_pci_link_get_object_status(link->handle, &sta);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get object status %s - %s\n",
-                   acpi_name(link->handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
+       /*
+        * Look up the PCI bus for the specified PCI bridge device.  Note
+        * that the PCI bridge device might not have any children yet.
+        * However, looking up its bus number doesn't require a valid child
+        * device, so we just pass NULL.
+        */
+       if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
+               device_printf(pcib, "Unable to read PCI bus number");
+               panic("PCI bridge without a bus number");
        }
 
-       if ((sta & ACPI_STA_ENABLE) == 0) {
-               kprintf("interrupt link %s is disabled\n",
-                   acpi_name(link->handle));
-               return_ACPI_STATUS (AE_ERROR);
+       /* Bump the reference count. */
+       ACPI_SERIAL_BEGIN(pci_link);
+       link = acpi_pci_link_lookup(dev, index);
+       if (link == NULL) {
+               device_printf(dev, "apparently invalid index %d\n", index);
+               ACPI_SERIAL_END(pci_link);
+               return;
        }
+       link->l_references++;
+       if (link->l_routed)
+               pci_link_interrupt_weights[link->l_irq]++;
 
-       error = acpi_pci_link_get_current_irq(link, &link->current_irq);
-       if (ACPI_FAILURE(error)) {
-               kprintf("couldn't get current IRQ from "
-                   "interrupt link %s - %s\n",
-                   acpi_name(link->handle), AcpiFormatException(error));
-               return_ACPI_STATUS (error);
+       /*
+        * The BIOS only routes interrupts via ISA IRQs using the ATPICs
+        * (8259As).  Thus, if this link is routed via an ISA IRQ, go
+        * look to see if the BIOS routed an IRQ for this link at the
+        * indicated (bus, slot, pin).  If so, we prefer that IRQ for
+        * this link and add that IRQ to our list of known-good IRQs.
+        * This provides a good work-around for link devices whose _CRS
+        * method is either broken or bogus.  We only use the value
+        * returned by _CRS if we can't find a valid IRQ via this method
+        * in fact.
+        *
+        * If this link is not routed via an ISA IRQ (because we are using
+        * APIC for example), then don't bother looking up the BIOS IRQ
+        * as if we find one it won't be valid anyway.
+        */
+       if (!link->l_isa_irq) {
+               ACPI_SERIAL_END(pci_link);
+               return;
        }
 
-       if (link->current_irq == irq) {
-               error = AE_OK;
-       } else {
-               kprintf("couldn't set IRQ %d to PCI interrupt link %d - %s\n",
-                   irq, link->current_irq, acpi_name(link->handle));
-               link->current_irq = 0;
-               error = AE_ERROR;
+       /* Try to find a BIOS IRQ setting from any matching devices. */
+       bios_irq = acpi_pci_link_search_irq(bus, slot, pin);
+       if (!PCI_INTERRUPT_VALID(bios_irq)) {
+               ACPI_SERIAL_END(pci_link);
+               return;
        }
 
-       return_ACPI_STATUS (error);
+       /* Validate the BIOS IRQ. */
+       if (!link_valid_irq(link, bios_irq)) {
+               device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
+                   bios_irq, (int)bus, slot, pin + 'A');
+       } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
+               link->l_bios_irq = bios_irq;
+               if (bios_irq < NUM_ISA_INTERRUPTS)
+                       pci_link_bios_isa_irqs |= (1 << bios_irq);
+               if (bios_irq != link->l_initial_irq &&
+                   PCI_INTERRUPT_VALID(link->l_initial_irq))
+                       device_printf(dev,
+                           "BIOS IRQ %u does not match initial IRQ %u\n",
+                           bios_irq, link->l_initial_irq);
+       } else if (bios_irq != link->l_bios_irq)
+               device_printf(dev,
+           "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
+                   bios_irq, (int)bus, slot, pin + 'A',
+                   link->l_bios_irq);
+       ACPI_SERIAL_END(pci_link);
 }
 
-/*
- * Auto arbitration for boot-disabled devices
- */
-
-static void
-acpi_pci_link_bootdisabled_dump(void)
+static ACPI_STATUS
+acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
 {
-       int                     i;
-       int                     irq;
-       struct acpi_pci_link_entry *link;
-
-       TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
-               /* boot-disabled link only. */
-               if (link->current_irq != 0)
-                       continue;
+       ACPI_RESOURCE *resource, *end, newres, *resptr;
+       ACPI_BUFFER crsbuf;
+       ACPI_STATUS status;
+       struct link *link;
+       int i, in_dpf;
+
+       /* Fetch the _CRS. */
+       ACPI_SERIAL_ASSERT(pci_link);
+       crsbuf.Pointer = NULL;
+       crsbuf.Length = ACPI_ALLOCATE_BUFFER;
+       status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), &crsbuf);
+       if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL)
+               status = AE_NO_MEMORY;
+       if (ACPI_FAILURE(status)) {
+               if (bootverbose)
+                       device_printf(sc->pl_dev,
+                           "Unable to fetch current resources: %s\n",
+                           AcpiFormatException(status));
+               return (status);
+       }
 
-               kprintf("%s:\n", acpi_name(link->handle));
-               kprintf("       interrupts:     ");
-               for (i = 0; i < link->number_of_interrupts; i++) {
-                       irq = link->sorted_irq[i];
-                       kprintf("%6d", irq);
+       /* Fill in IRQ resources via link structures. */
+       srsbuf->Pointer = NULL;
+       link = sc->pl_links;
+       i = 0;
+       in_dpf = DPF_OUTSIDE;
+       resource = (ACPI_RESOURCE *)crsbuf.Pointer;
+       end = (ACPI_RESOURCE *)((char *)crsbuf.Pointer + crsbuf.Length);
+       for (;;) {
+               switch (resource->Type) {
+               case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+                       switch (in_dpf) {
+                       case DPF_OUTSIDE:
+                               /* We've started the first DPF. */
+                               in_dpf = DPF_FIRST;
+                               break;
+                       case DPF_FIRST:
+                               /* We've started the second DPF. */
+                               panic(
+               "%s: Multiple dependent functions within a current resource",
+                                   __func__);
+                               break;
+                       }
+                       resptr = NULL;
+                       break;
+               case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+                       /* We are finished with DPF parsing. */
+                       KASSERT(in_dpf != DPF_OUTSIDE,
+                           ("%s: end dpf when not parsing a dpf", __func__));
+                       in_dpf = DPF_OUTSIDE;
+                       resptr = NULL;
+                       break;
+               case ACPI_RESOURCE_TYPE_IRQ:
+                       MPASS(i < sc->pl_num_links);
+                       MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ);
+                       newres = link->l_prs_template;
+                       resptr = &newres;
+                       resptr->Data.Irq.InterruptCount = 1;
+                       if (PCI_INTERRUPT_VALID(link->l_irq)) {
+                               KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
+               ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
+                                   __func__, link->l_irq));
+                               resptr->Data.Irq.Interrupts[0] = link->l_irq;
+                       } else
+                               resptr->Data.Irq.Interrupts[0] = 0;
+                       link++;
+                       i++;
+                       break;
+               case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+                       MPASS(i < sc->pl_num_links);
+                       MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ);
+                       newres = link->l_prs_template;
+                       resptr = &newres;
+                       resptr->Data.ExtendedIrq.InterruptCount = 1;
+                       if (PCI_INTERRUPT_VALID(link->l_irq))
+                               resptr->Data.ExtendedIrq.Interrupts[0] =
+                                   link->l_irq;
+                       else
+                               resptr->Data.ExtendedIrq.Interrupts[0] = 0;
+                       link++;
+                       i++;
+                       break;
+               default:
+                       resptr = resource;
                }
-               kprintf("\n");
-               kprintf("       penalty:        ");
-               for (i = 0; i < link->number_of_interrupts; i++) {
-                       irq = link->sorted_irq[i];
-                       kprintf("%6d", irq_penalty[irq]);
+               if (resptr != NULL) {
+                       status = acpi_AppendBufferResource(srsbuf, resptr);
+                       if (ACPI_FAILURE(status)) {
+                               device_printf(sc->pl_dev,
+                                   "Unable to build resources: %s\n",
+                                   AcpiFormatException(status));
+                               if (srsbuf->Pointer != NULL)
+                                       AcpiOsFree(srsbuf->Pointer);
+                               AcpiOsFree(crsbuf.Pointer);
+                               return (status);
+                       }
                }
-               kprintf("\n");
-               kprintf("       references:     %d\n", link->references);
-               kprintf("       priority:       %d\n", link->priority);
+               if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
+                       break;
+               resource = ACPI_NEXT_RESOURCE(resource);
+               if (resource >= end)
+                       break;
        }
+       AcpiOsFree(crsbuf.Pointer);
+       return (AE_OK);
 }
 
-static void
-acpi_pci_link_init_irq_penalty(void)
+static ACPI_STATUS
+acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
+    ACPI_BUFFER *srsbuf)
 {
-       int                     irq;
-
-       bzero(irq_penalty, sizeof(irq_penalty));
-       for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) {
-               /* 0, 1, 2, 8:  timer, keyboard, cascade */
-               if (irq == 0 || irq == 1 || irq == 2 || irq == 8) {
-                       irq_penalty[irq] = 100000;
-                       continue;
-               }
-
-               /* 13, 14, 15:  npx, ATA controllers */
-               if (irq == 13 || irq == 14 || irq == 15) {
-                       irq_penalty[irq] = 10000;
-                       continue;
+       ACPI_RESOURCE newres;
+       ACPI_STATUS status;
+       struct link *link;
+       int i;
+
+       /* Start off with an empty buffer. */
+       srsbuf->Pointer = NULL;
+       link = sc->pl_links;
+       for (i = 0; i < sc->pl_num_links; i++) {
+
+               /* Add a new IRQ resource from each link. */
+               link = &sc->pl_links[i];
+               newres = link->l_prs_template;
+               if (newres.Type == ACPI_RESOURCE_TYPE_IRQ) {
+
+                       /* Build an IRQ resource. */
+                       newres.Data.Irq.InterruptCount = 1;
+                       if (PCI_INTERRUPT_VALID(link->l_irq)) {
+                               KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
+               ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
+                                   __func__, link->l_irq));
+                               newres.Data.Irq.Interrupts[0] = link->l_irq;
+                       } else
+                               newres.Data.Irq.Interrupts[0] = 0;
+               } else {
+
+                       /* Build an ExtIRQ resuorce. */
+                       newres.Data.ExtendedIrq.InterruptCount = 1;
+                       if (PCI_INTERRUPT_VALID(link->l_irq))
+                               newres.Data.ExtendedIrq.Interrupts[0] =
+                                   link->l_irq;
+                       else
+                               newres.Data.ExtendedIrq.Interrupts[0] = 0;
                }
 
-               /* 3,4,6,7,12:  typicially used by legacy hardware */
-               if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) {
-                       irq_penalty[irq] = 1000;
-                       continue;
+               /* Add the new resource to the end of the _SRS buffer. */
+               status = acpi_AppendBufferResource(srsbuf, &newres);
+               if (ACPI_FAILURE(status)) {
+                       device_printf(sc->pl_dev,
+                           "Unable to build resources: %s\n",
+                           AcpiFormatException(status));
+                       if (srsbuf->Pointer != NULL)
+                               AcpiOsFree(srsbuf->Pointer);
+                       return (status);
                }
        }
+       return (AE_OK);
 }
 
-static int
-link_exclusive(ACPI_RESOURCE *res)
-{
-       if (res == NULL ||
-           (res->Type != ACPI_RESOURCE_TYPE_IRQ &&
-           res->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ))
-               return (0);
-
-       if ((res->Type == ACPI_RESOURCE_TYPE_IRQ &&
-           res->Data.Irq.Sharable == ACPI_EXCLUSIVE) ||
-           (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ &&
-           res->Data.ExtendedIrq.Sharable == ACPI_EXCLUSIVE))
-               return (1);
-
-       return (0);
-}
-
-static void
-acpi_pci_link_update_irq_penalty(device_t dev, int busno)
+static ACPI_STATUS
+acpi_pci_link_route_irqs(device_t dev)
 {
-       int                     i;
-       int                     irq;
-       int                     rid;
-       struct resource         *res;
-       struct acpi_prt_entry   *entry;
-       struct acpi_pci_link_entry *link;
-
-       TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
-               if (entry->busno != busno)
-                       continue;
-
-               /* Impossible? */
-               link = entry->pci_link;
-               if (link == NULL)
-                       continue;
-
-               if (link->current_irq != 0) {
-                       /* not boot-disabled link, we will use this IRQ. */
-                       irq_penalty[link->current_irq] += 100;
-                       continue;
-               }
+       struct acpi_pci_link_softc *sc;
+       ACPI_RESOURCE *resource, *end;
+       ACPI_BUFFER srsbuf;
+       ACPI_STATUS status;
+       struct link *link;
+       int i;
+
+       ACPI_SERIAL_ASSERT(pci_link);
+       sc = device_get_softc(dev);
+       if (sc->pl_crs_bad)
+               status = acpi_pci_link_srs_from_links(sc, &srsbuf);
+       else
+               status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
+
+       /* Write out new resources via _SRS. */
+       status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "Unable to route IRQs: %s\n",
+                   AcpiFormatException(status));
+               AcpiOsFree(srsbuf.Pointer);
+               return (status);
+       }
 
-               /* boot-disabled link */
-               for (i = 0; i < link->number_of_interrupts; i++) {
-                       /* give 10 for each possible IRQs. */
-                       irq = link->interrupts[i];
-                       irq_penalty[irq] += 10;
-
-                       /* higher penalty if exclusive. */
-                       if (link_exclusive(&link->possible_resources))
-                               irq_penalty[irq] += 100;
-
-                       /* XXX try to get this IRQ in non-sharable mode. */
-                       rid = 0;
-                       res = bus_alloc_resource(dev, SYS_RES_IRQ,
-                                                &rid, irq, irq, 1, 0);
-                       if (res != NULL) {
-                               bus_release_resource(dev, SYS_RES_IRQ,
-                                   rid, res);
-                       } else {
-                               /* this is in use, give 100. */
-                               irq_penalty[irq] += 100;
+       /*
+        * Perform acpi_config_intr() on each IRQ resource if it was just
+        * routed for the first time.
+        */
+       link = sc->pl_links;
+       i = 0;
+       resource = (ACPI_RESOURCE *)srsbuf.Pointer;
+       end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
+       for (;;) {
+               if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
+                       break;
+               switch (resource->Type) {
+               case ACPI_RESOURCE_TYPE_IRQ:
+               case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+                       MPASS(i < sc->pl_num_links);
+
+                       /*
+                        * Only configure the interrupt and update the
+                        * weights if this link has a valid IRQ and was
+                        * previously unrouted.
+                        */
+                       if (!link->l_routed &&
+                           PCI_INTERRUPT_VALID(link->l_irq)) {
+                               link->l_routed = TRUE;
+                               acpi_config_intr(dev, resource);
+                               pci_link_interrupt_weights[link->l_irq] +=
+                                   link->l_references;
                        }
+                       link++;
+                       i++;
+                       break;
                }
-
-               /* initialize `sorted' possible IRQs. */
-               bcopy(link->interrupts, link->sorted_irq,
-                   sizeof(link->sorted_irq));
+               resource = ACPI_NEXT_RESOURCE(resource);
+               if (resource >= end)
+                       break;
        }
+       AcpiOsFree(srsbuf.Pointer);
+       return (AE_OK);
 }
 
-static void
-acpi_pci_link_set_bootdisabled_priority(void)
+static int
+acpi_pci_link_resume(device_t dev)
 {
-       int                     sum_penalty;
-       int                     i;
-       int                     irq;
-       struct acpi_pci_link_entry *link, *link_pri;
-       TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
-
-       if (bootverbose) {
-               kprintf("ACPI PCI link before setting link priority:\n");
-               acpi_pci_link_bootdisabled_dump();
-       }
+       struct acpi_pci_link_softc *sc;
+       ACPI_STATUS status;
+       int i, routed;
 
-       /* reset priority for all links. */
-       TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
-               link->priority = 0;
+       /*
+        * If all of our links are routed, then restore the link via _SRS,
+        * otherwise, disable the link via _DIS.
+        */
+       ACPI_SERIAL_BEGIN(pci_link);
+       sc = device_get_softc(dev);
+       routed = 0;
+       for (i = 0; i < sc->pl_num_links; i++)
+               if (sc->pl_links[i].l_routed)
+                       routed++;
+       if (routed == sc->pl_num_links)
+               status = acpi_pci_link_route_irqs(dev);
+       else {
+               AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL);
+               status = AE_OK;
+       }
+       ACPI_SERIAL_END(pci_link);
+       if (ACPI_FAILURE(status))
+               return (ENXIO);
+       else
+               return (0);
+}
 
-       TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
-               /* not boot-disabled link, give no chance to be arbitrated. */
-               if (link->current_irq != 0) {
-                       link->priority = 0;
-                       continue;
+/*
+ * Pick an IRQ to use for this unrouted link.
+ */
+static uint8_t
+acpi_pci_link_choose_irq(device_t dev, struct link *link)
+{
+       char tunable_buffer[64], link_name[5];
+       u_int8_t best_irq, pos_irq;
+       int best_weight, pos_weight, i;
+
+       KASSERT(!link->l_routed, ("%s: link already routed", __func__));
+       KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
+           ("%s: link already has an IRQ", __func__));
+
+       /* Check for a tunable override. */
+       if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
+           sizeof(link_name)))) {
+               ksnprintf(tunable_buffer, sizeof(tunable_buffer),
+                   "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
+               if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
+                       if (!link_valid_irq(link, i))
+                               device_printf(dev,
+                                   "Warning, IRQ %d is not listed as valid\n",
+                                   i);
+                       return (i);
                }
-
-               /*
-                * Calculate the priority for each boot-disabled links.
-                * o IRQ penalty indicates difficulty to use. 
-                * o #references for devices indicates importance of the link.
-                * o #interrupts indicates flexibility of the link.
-                */
-               sum_penalty = 0;
-               for (i = 0; i < link->number_of_interrupts; i++) {
-                       irq = link->interrupts[i];
-                       sum_penalty += irq_penalty[irq];
+               ksnprintf(tunable_buffer, sizeof(tunable_buffer),
+                   "hw.pci.link.%s.irq", link_name);
+               if (kgetenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
+                       if (!link_valid_irq(link, i))
+                               device_printf(dev,
+                                   "Warning, IRQ %d is not listed as valid\n",
+                                   i);
+                       return (i);
                }
-
-               link->priority = (sum_penalty * link->references) /
-                   link->number_of_interrupts;
        }
 
        /*
-        * Sort PCI links based on the priority.
-        * XXX Any other better ways rather than using work list?
+        * If we have a valid BIOS IRQ, use that.  We trust what the BIOS
+        * says it routed over what _CRS says the link thinks is routed.
         */
-       TAILQ_INIT(&sorted_list);
-       while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
-               link = TAILQ_FIRST(&acpi_pci_link_entries);
-               /* find an entry which has the highest priority. */
-               TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
-                       if (link->priority < link_pri->priority)
-                               link = link_pri;
-
-               /* move to work list. */
-               TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
-               TAILQ_INSERT_TAIL(&sorted_list, link, links);
-       }
+       if (PCI_INTERRUPT_VALID(link->l_bios_irq))
+               return (link->l_bios_irq);
 
-       while (!TAILQ_EMPTY(&sorted_list)) {
-               /* move them back to the list, one by one... */
-               link = TAILQ_FIRST(&sorted_list);
-               TAILQ_REMOVE(&sorted_list, link, links);
-               TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
-       }
-}
-
-static void
-acpi_pci_link_fixup_bootdisabled_link(void)
-{
-       int                     i, j;
-       int                     irq1, irq2;
-       struct acpi_pci_link_entry *link;
-       ACPI_STATUS             error;
-
-       if (bootverbose) {
-               kprintf("ACPI PCI link before fixup for boot-disabled links:\n");
-               acpi_pci_link_bootdisabled_dump();
-       }
+       /*
+        * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
+        * then use that.
+        */
+       if (PCI_INTERRUPT_VALID(link->l_initial_irq))
+               return (link->l_initial_irq);
 
-       TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
-               /* ignore non boot-disabled links. */
-               if (link->current_irq != 0)
+       /*
+        * Ok, we have no useful hints, so we have to pick from the
+        * possible IRQs.  For ISA IRQs we only use interrupts that
+        * have already been used by the BIOS.
+        */
+       best_irq = PCI_INVALID_IRQ;
+       best_weight = INT_MAX;
+       for (i = 0; i < link->l_num_irqs; i++) {
+               pos_irq = link->l_irqs[i];
+               if (pos_irq < NUM_ISA_INTERRUPTS &&
+                   (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
                        continue;
-
-               /* sort IRQs based on their penalty descending. */
-               for (i = 0; i < link->number_of_interrupts; i++) {
-                       irq1 = link->sorted_irq[i];
-                       for (j = i + 1; j < link->number_of_interrupts; j++) {
-                               irq2 = link->sorted_irq[j];
-                               if (irq_penalty[irq1] < irq_penalty[irq2]) {
-                                       continue;
-                               }
-                               link->sorted_irq[i] = irq2;
-                               link->sorted_irq[j] = irq1;
-                               irq1 = irq2;
-                       }
+               pos_weight = pci_link_interrupt_weights[pos_irq];
+               if (pos_weight < best_weight) {
+                       best_weight = pos_weight;
+                       best_irq = pos_irq;
                }
+       }
 
-               /* try with lower penalty IRQ. */
-               for (i = 0; i < link->number_of_interrupts; i++) {
-                       irq1 = link->sorted_irq[i];
-                       error = acpi_pci_link_set_irq(link, irq1);
-                       if (error == AE_OK) {
-                               /* OK, we use this.  give another penalty. */
-                               irq_penalty[irq1] += 100 * link->references;
-                               break;
-                       }
+       /*
+        * If this is an ISA IRQ, try using the SCI if it is also an ISA
+        * interrupt as a fallback.
+        */
+       if (link->l_isa_irq) {
+               pos_irq = AcpiGbl_FADT.SciInterrupt;
+               pos_weight = pci_link_interrupt_weights[pos_irq];
+               if (pos_weight < best_weight) {
+                       best_weight = pos_weight;
+                       best_irq = pos_irq;
                }
        }
 
-       if (bootverbose) {
-               kprintf("ACPI PCI link after fixup for boot-disabled links:\n");
-               acpi_pci_link_bootdisabled_dump();
-       }
+       if (PCI_INTERRUPT_VALID(best_irq)) {
+               if (bootverbose)
+                       device_printf(dev, "Picked IRQ %u with weight %d\n",
+                           best_irq, best_weight);
+       } else
+               device_printf(dev, "Unable to choose an IRQ\n");
+       return (best_irq);
 }
 
-/*
- * Public interface
- */
-
 int
-acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
+acpi_pci_link_route_interrupt(device_t dev, int index)
 {
-       struct acpi_prt_entry   *entry;
-       ACPI_PCI_ROUTING_TABLE  *prt;
-       u_int8_t                *prtp;
-       ACPI_STATUS             error;
-       static int              first_time =1;
-
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+       struct link *link;
 
        if (acpi_disabled("pci_link"))
-               return (0);
-
-       if (first_time) {
-               TAILQ_INIT(&acpi_prt_entries);
-               TAILQ_INIT(&acpi_pci_link_entries);
-               acpi_pci_link_init_irq_penalty();
-               first_time = 0;
-       }
+               return (PCI_INVALID_IRQ);
 
-       if (prtbuf == NULL)
-               return (-1);
-
-       prtp = prtbuf->Pointer;
-       if (prtp == NULL)               /* didn't get routing table */
-               return (-1);
-
-       /* scan the PCI Routing Table */
-       for (;;) {
-               prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
-
-               if (prt->Length == 0)   /* end of table */
-                   break;
-
-               error = acpi_pci_link_add_prt(dev, prt, busno);
-               if (ACPI_FAILURE(error)) {
-                       kprintf("couldn't add PCI interrupt link entry - %s\n",
-                           AcpiFormatException(error));
-               }
-
-               /* skip to next entry */
-               prtp += prt->Length;
-       }
+       ACPI_SERIAL_BEGIN(pci_link);
+       link = acpi_pci_link_lookup(dev, index);
+       if (link == NULL)
+               panic("%s: apparently invalid index %d", __func__, index);
 
-       if (bootverbose) {
-               kprintf("ACPI PCI link initial configuration:\n");
-               TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
-                       if (entry->busno != busno)
-                               continue;
-                       acpi_pci_link_entry_dump(entry);
-               }
+       /*
+        * If this link device is already routed to an interrupt, just return
+        * the interrupt it is routed to.
+        */
+       if (link->l_routed) {
+               KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
+                   ("%s: link is routed but has an invalid IRQ", __func__));
+               ACPI_SERIAL_END(pci_link);
+               return (link->l_irq);
        }
 
-       /* manual configuration. */
-       TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
-               int                     irq;
-               char                    prthint[32];
-
-               if (entry->busno != busno)
-                       continue;
-
-               ksnprintf(prthint, sizeof(prthint),
-                   "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
-                   (int)((entry->prt.Address & 0xffff0000) >> 16),
-                   (int)entry->prt.Pin);
-
-               if (kgetenv_int(prthint, &irq) == 0)
-                       continue;
-
-               if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
-                       error = acpi_pci_link_set_irq(entry->pci_link, irq);
-                       if (ACPI_FAILURE(error)) {
-                               kprintf("couldn't set IRQ to "
-                                   "link entry %s - %s\n",
-                                   acpi_name(entry->pci_link->handle),
-                                   AcpiFormatException(error));
-                       }
-                       continue;
-               }
+       /* Choose an IRQ if we need one. */
+       if (!PCI_INTERRUPT_VALID(link->l_irq)) {
+               link->l_irq = acpi_pci_link_choose_irq(dev, link);
 
                /*
-                * Do auto arbitration for this device's PCI link
-                * if hint value 0 is specified.
+                * Try to route the interrupt we picked.  If it fails, then
+                * assume the interrupt is not routed.
                 */
-               if (irq == 0)
-                       entry->pci_link->current_irq = 0;
-       }
-
-       /* auto arbitration */
-       acpi_pci_link_update_irq_penalty(dev, busno);
-       acpi_pci_link_set_bootdisabled_priority();
-       acpi_pci_link_fixup_bootdisabled_link();
-
-       if (bootverbose) {
-               kprintf("ACPI PCI link arbitrated configuration:\n");
-               TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
-                       if (entry->busno != busno)
-                               continue;
-                       acpi_pci_link_entry_dump(entry);
+               if (PCI_INTERRUPT_VALID(link->l_irq)) {
+                       acpi_pci_link_route_irqs(dev);
+                       if (!link->l_routed)
+                               link->l_irq = PCI_INVALID_IRQ;
                }
        }
-
-       return (0);
+       ACPI_SERIAL_END(pci_link);
+       return (link->l_irq);
 }
 
-int
-acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
+/*
+ * This is gross, but we abuse the identify routine to perform one-time
+ * SYSINIT() style initialization for the driver.
+ */
+static void
+acpi_pci_link_identify(driver_t *driver, device_t parent)
 {
-       struct acpi_prt_entry   *entry;
-       ACPI_STATUS             error;
 
-       ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+       /*
+        * If the SCI is an ISA IRQ, add it to the bitmask of known good
+        * ISA IRQs.
+        *
+        * XXX: If we are using the APIC, the SCI might have been
+        * rerouted to an APIC pin in which case this is invalid.  However,
+        * if we are using the APIC, we also shouldn't be having any PCI
+        * interrupts routed via ISA IRQs, so this is probably ok.
+        */
+       if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS)
+               pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt);
+}
+
+static device_method_t acpi_pci_link_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_identify,      acpi_pci_link_identify),
+       DEVMETHOD(device_probe,         acpi_pci_link_probe),
+       DEVMETHOD(device_attach,        acpi_pci_link_attach),
+       DEVMETHOD(device_resume,        acpi_pci_link_resume),
 
-       if (acpi_disabled("pci_link"))
-               return (0);
+       {0, 0}
+};
 
-       TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
-               if (entry->pcidev != dev)
-                       continue;
+static driver_t acpi_pci_link_driver = {
+       "pci_link",
+       acpi_pci_link_methods,
+       sizeof(struct acpi_pci_link_softc),
+};
 
-               error = acpi_pci_link_set_irq(entry->pci_link,
-                           entry->pci_link->current_irq);
-               if (ACPI_FAILURE(error)) {
-                       kprintf("couldn't set IRQ to link entry %s - %s\n",
-                           acpi_name(entry->pci_link->handle),
-                           AcpiFormatException(error));
-               }
-       }
+static devclass_t pci_link_devclass;
 
-       return (0);
-}
+DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, pci_link_devclass, 0,
+    0);
+MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1);
index e5305d6..acab302 100644 (file)
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/dev/acpica/acpi_pcib.c,v 1.43 2004/05/06 02:18:58 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pcib.c,v 1.3 2006/12/22 23:26:14 swildner Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pcib.c,v 1.60.8.1 2009/04/15 03:14:26 kensmith Exp $");
  */
 
+#include <sys/cdefs.h>
+
 #include "opt_acpi.h"
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <machine/smp.h>
 
 #include "acpi.h"
-#include "accommon.h"
-#include "acpivar.h"
-#include "acpi_pcibvar.h"
+#include <dev/acpica5/acpivar.h>
+#include <dev/acpica5/acpi_pcibvar.h>
 
 #include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
 #include "pcib_if.h"
 
 /* Hooks for the ACPI CA debugging infrastructure. */
 #define _COMPONENT     ACPI_BUS
 ACPI_MODULE_NAME("PCI")
 
+ACPI_SERIAL_DECL(pcib, "ACPI PCI bus methods");
+
+/*
+ * For locking, we assume the caller is not concurrent since this is
+ * triggered by newbus methods.
+ */
+
+struct prt_lookup_request {
+    ACPI_PCI_ROUTING_TABLE *pr_entry;
+    u_int      pr_pin;
+    u_int      pr_slot;
+};
+
+typedef        void prt_entry_handler(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
+
+static void    prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
+static void    prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
+static void    prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler,
+                   void *arg);
+
+#define PCI_INVALID_IRQ 255
+
+static void
+prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler, void *arg)
+{
+    ACPI_PCI_ROUTING_TABLE *entry;
+    char *prtptr;
+
+    /* First check to see if there is a table to walk. */
+    if (prt == NULL || prt->Pointer == NULL)
+       return;
+
+    /* Walk the table executing the handler function for each entry. */
+    prtptr = prt->Pointer;
+    entry = (ACPI_PCI_ROUTING_TABLE *)prtptr;
+    while (entry->Length != 0) {
+       handler(entry, arg);
+       prtptr += entry->Length;
+       entry = (ACPI_PCI_ROUTING_TABLE *)prtptr;
+    }
+}
+
+static void
+prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg)
+{
+    ACPI_HANDLE handle;
+    device_t child, pcib;
+    int error;
+
+    /* We only care about entries that reference a link device. */
+    if (entry->Source == NULL || entry->Source[0] == '\0')
+       return;
+
+    /*
+     * In practice, we only see SourceIndex's of 0 out in the wild.
+     * When indices != 0 have been found, they've been bugs in the ASL.
+     */
+    if (entry->SourceIndex != 0)
+       return;
+
+    /* Lookup the associated handle and device. */
+    pcib = (device_t)arg;
+    if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, entry->Source, &handle)))
+       return;
+    child = acpi_get_device(handle);
+    if (child == NULL)
+       return;
+
+    /* If the device hasn't been probed yet, force it to do so. */
+    error = device_probe_and_attach(child);
+    if (error != 0) {
+       device_printf(pcib, "failed to force attach of %s\n",
+           acpi_name(handle));
+       return;
+    }
+
+    /* Add a reference for a specific bus/device/pin tuple. */
+    acpi_pci_link_add_reference(child, entry->SourceIndex, pcib,
+       ACPI_ADR_PCI_SLOT(entry->Address), entry->Pin);
+}
+
 int
 acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
 {
@@ -60,16 +142,19 @@ acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
      * XXX: This isn't entirely correct since we may be a PCI bus
      * on a hot-plug docking station, etc.
      */
+
     if (!acpi_DeviceIsPresent(dev))
        return_VALUE(ENXIO);
 
     /*
      * Get the PCI interrupt routing table for this bus.  If we can't
-     * get it, this is not an error but may reduce functionality.
+     * get it, this is not an error but may reduce functionality.  There
+     * are several valid bridges in the field that do not have a _PRT, so
+     * only warn about missing tables if bootverbose is set.
      */
     prt->Length = ACPI_ALLOCATE_BUFFER;
     status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt);
-    if (ACPI_FAILURE(status))
+    if (ACPI_FAILURE(status) && (bootverbose || status != AE_NOT_FOUND))
        device_printf(dev,
            "could not get PCI interrupt routing table for %s - %s\n",
            acpi_name(acpi_get_handle(dev)), AcpiFormatException(status));
@@ -85,318 +170,144 @@ acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
     /*
      * Now go scan the bus.
      */
-    acpi_pci_link_config(dev, prt, busno);
+    prt_walk_table(prt, prt_attach_devices, dev);
 
     return_VALUE (bus_generic_attach(dev));
 }
 
 int
-acpi_pcib_resume(device_t dev, ACPI_BUFFER *prt, int busno)
+acpi_pcib_resume(device_t dev)
 {
-    acpi_pci_link_resume(dev, prt, busno);
+
     return (bus_generic_resume(dev));
 }
 
+static void
+prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg)
+{
+    struct prt_lookup_request *pr;
+
+    pr = (struct prt_lookup_request *)arg;
+    if (pr->pr_entry != NULL)
+       return;
+
+    /*
+     * Compare the slot number (high word of Address) and pin number
+     * (note that ACPI uses 0 for INTA) to check for a match.
+     *
+     * Note that the low word of the Address field (function number)
+     * is required by the specification to be 0xffff.  We don't risk
+     * checking it here.
+     */
+    if (ACPI_ADR_PCI_SLOT(entry->Address) == pr->pr_slot &&
+       entry->Pin == pr->pr_pin)
+           pr->pr_entry = entry;
+}
+
 /*
  * Route an interrupt for a child of the bridge.
- *
- * XXX clean up error messages
- *
- * XXX this function is somewhat bulky
  */
 int
 acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
     ACPI_BUFFER *prtbuf)
 {
-    ACPI_PCI_ROUTING_TABLE     *prt;
-    ACPI_HANDLE                        lnkdev;
-    ACPI_BUFFER                        crsbuf, prsbuf, buf;
-    ACPI_RESOURCE              *crsres, *prsres, resbuf;
-    ACPI_DEVICE_INFO           *devinfo;
-    ACPI_STATUS                        status;
-    UINT32                     InterruptCount, intr;
-    u_int8_t                   *prtp;
-    int                                interrupt;
-    int                                i;
+    ACPI_PCI_ROUTING_TABLE *prt;
+    struct prt_lookup_request pr;
+    ACPI_HANDLE lnkdev;
+    int interrupt;
 
     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-    
-    buf.Pointer = NULL;
-    crsbuf.Pointer = NULL;
-    prsbuf.Pointer = NULL;
-    interrupt = 255;
-    intr = 0;
+
+    interrupt = PCI_INVALID_IRQ;
 
     /* ACPI numbers pins 0-3, not 1-4 like the BIOS. */
     pin--;
 
-    /* We failed to retrieve the routing table. */
-    prtp = prtbuf->Pointer;
-    if (prtp == NULL)
-       goto out;
+    ACPI_SERIAL_BEGIN(pcib);
 
-    /* Scan the table to look for this device. */
-    for (;;) {
-       prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
-
-       /* We hit the end of the table. */
-       if (prt->Length == 0)
-           goto out;
-
-       /*
-        * Compare the slot number (high word of Address) and pin number
-        * (note that ACPI uses 0 for INTA) to check for a match.
-        *
-        * Note that the low word of the Address field (function number)
-        * is required by the specification to be 0xffff.  We don't risk
-        * checking it here.
-        */
-       if (((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev) &&
-           prt->Pin == pin) {
-           if (bootverbose)
-               device_printf(pcib, "matched entry for %d.%d.INT%c (src %s)\n",
-                             pci_get_bus(dev), pci_get_slot(dev), 'A' + pin,
-                             prt->Source);
-           break;
-       }
-       
-       /* Skip to the next entry. */
-       prtp += prt->Length;
-    }
-
-    /*
-     * If source is empty/NULL, the source index is the global IRQ number.
-     */
-    if (prt->Source == NULL || prt->Source[0] == '\0') {
-       if (bootverbose)
-           device_printf(pcib, "device is hardwired to IRQ %d\n",
-                         prt->SourceIndex);
-       interrupt = prt->SourceIndex;
+    /* Search for a matching entry in the routing table. */
+    pr.pr_entry = NULL;
+    pr.pr_pin = pin;
+    pr.pr_slot = pci_get_slot(dev);
+    prt_walk_table(prtbuf, prt_lookup_device, &pr);
+    if (pr.pr_entry == NULL) {
+       device_printf(pcib, "no PRT entry for %d.%d.INT%c\n", pci_get_bus(dev),
+           pci_get_slot(dev), 'A' + pin);
        goto out;
     }
-    
-    /*
-     * We have to find the source device (PCI interrupt link device).
-     */
-    if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
-       device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
-                     prt->Source);
-       goto out;
+    prt = pr.pr_entry;
+
+    if (bootverbose) {
+       device_printf(pcib, "matched entry for %d.%d.INT%c",
+           pci_get_bus(dev), pci_get_slot(dev), 'A' + pin);
+       if (prt->Source != NULL && prt->Source[0] != '\0')
+               kprintf(" (src %s:%u)", prt->Source, prt->SourceIndex);
+       kprintf("\n");
     }
 
     /*
-     * Verify that this is a PCI link device and that it's present.
-     */
-    buf.Length = ACPI_ALLOCATE_BUFFER;
-    if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &buf))) {
-       device_printf(pcib, "couldn't validate PCI interrupt link device %s\n",
-                     prt->Source);
-       goto out;
-    }
-    devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
-    if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
-       strcmp("PNP0C0F", devinfo->HardwareId.Value) != 0) {
-       device_printf(pcib, "PCI interrupt link %s has invalid _HID (%s)\n",
-                     prt->Source, devinfo->HardwareId.Value);
-       goto out;
-    }
-    if ((devinfo->Valid & ACPI_VALID_STA) != 0 &&
-       (devinfo->CurrentStatus & 0x9) != 0x9) {
-       device_printf(pcib, "PCI interrupt link device %s not present\n",
-                     prt->Source);
-       goto out;
-    }
-
-    /*
-     * Get the current and possible resources for the interrupt link device.
-     * If we fail to get the current resources, this is a fatal error.
-     */
-    crsbuf.Length = ACPI_ALLOCATE_BUFFER;
-    if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) {
-       device_printf(pcib, "PCI interrupt link device _CRS failed - %s\n",
-                     AcpiFormatException(status));
-       goto out;
-    }
-    prsbuf.Length = ACPI_ALLOCATE_BUFFER;
-    if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) {
-       device_printf(pcib, "PCI interrupt link device _PRS failed - %s\n",
-                     AcpiFormatException(status));
-    }
-    ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._CRS\n",
-                    (long)crsbuf.Length, acpi_name(lnkdev)));
-    ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._PRS\n",
-                    (long)prsbuf.Length, acpi_name(lnkdev)));
-
-    /*
-     * The interrupt may already be routed, so check _CRS first.  We don't
-     * check the 'decoding' bit in the _STA result, since there's nothing in
-     * the spec that mandates it be set, however some BIOS' will set it if
-     * the decode is active.
+     * If source is empty/NULL, the source index is a global IRQ number
+     * and it's hard-wired so we're done.
      *
-     * The Source Index points to the particular resource entry we're
-     * interested in.
+     * XXX: If the source index is non-zero, ignore the source device and
+     * assume that this is a hard-wired entry.
      */
-    if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex,
-       &crsres))) {
-       device_printf(pcib, "_CRS buffer corrupt, cannot route interrupt\n");
-       goto out;
-    }
-
-    /* Type-check the resource we've found. */
-    if (crsres->Type != ACPI_RESOURCE_TYPE_IRQ && crsres->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
-       device_printf(pcib, "_CRS resource entry has unsupported type %d\n",
-                     crsres->Type);
-       goto out;
-    }
-
-    /* Set variables based on resource type. */
-    if (crsres->Type == ACPI_RESOURCE_TYPE_IRQ) {
-       InterruptCount = crsres->Data.Irq.InterruptCount;
-       if (InterruptCount >= 1)
-           intr = crsres->Data.Irq.Interrupts[0];
-    } else {
-       InterruptCount = crsres->Data.ExtendedIrq.InterruptCount;
-       if (InterruptCount >= 1)
-           intr = crsres->Data.ExtendedIrq.Interrupts[0];
-    }
-
-    /* If there's more than one interrupt, this is an error. */
-    if (InterruptCount > 1) {
-       device_printf(pcib, "device has too many interrupts (%d)\n",
-                     InterruptCount);
-       goto out;
-    }
-
-    /* 
-     * If there's only one interrupt, and it's not zero, then it's already
-     * routed.
-     *
-     * Note that we could also check the 'decoding' bit in _STA, but can't
-     * depend on it since it's not part of the spec.
-     *
-     * XXX check ASL examples to see if this is an acceptable set of tests
-     */
-    if (InterruptCount == 1 && intr != 0) {
-       device_printf(pcib, "slot %d INT%c is routed to irq %d\n",
-                     pci_get_slot(dev), 'A' + pin, intr);
-       interrupt = intr;
-       goto out;
-    }
-    
-    /* 
-     * There isn't an interrupt, so we have to look at _PRS to get one.
-     * Get the set of allowed interrupts from the _PRS resource indexed
-     * by SourceIndex.
-     */
-    if (prsbuf.Pointer == NULL) {
-       device_printf(pcib, "no routed irq and no _PRS on irq link device\n");
+    if (prt->Source == NULL || prt->Source[0] == '\0' ||
+       prt->SourceIndex != 0) {
+       if (bootverbose)
+           device_printf(pcib, "slot %d INT%c hardwired to IRQ %d\n",
+               pci_get_slot(dev), 'A' + pin, prt->SourceIndex);
+       if (prt->SourceIndex) {
+           interrupt = prt->SourceIndex;
+#if defined(APIC_IO)
+       int line;
+        line = pci_apic_irq(pci_get_bus(dev), pci_get_slot(dev), pin+1, interrupt);
+        if (line >= 0) {
+kprintf("apic: try line %d\n", line);
+                return line;
+        } else {
+                int irq = pci_get_irq(dev);
+
+                /*
+                 * PCI interrupts might be redirected to the
+                 * ISA bus according to some MP tables.  Use the
+                 * same methods as used by the ISA devices
+                 * devices to find the proper IOAPIC int pin.
+                 */
+                kprintf("ACPI: Try routing through ISA bus for "
+                        "bus %d slot %d INT%c irq %d\n",
+                        pci_get_bus(dev), pci_get_slot(dev), 'A' + pin+1, irq);
+                line = isa_apic_irq(irq);
+        }
+       interrupt = line;
+#endif
+       pci_write_config(dev, PCIR_INTLINE, interrupt, 1);
+           BUS_CONFIG_INTR(dev, interrupt, INTR_TRIGGER_LEVEL,
+               INTR_POLARITY_LOW);
+       } else
+           device_printf(pcib, "error: invalid hard-wired IRQ of 0\n");
        goto out;
     }
 
     /*
-     * Search through the _PRS resources, looking for an IRQ or extended
-     * IRQ resource.  Skip dependent function resources for now.  In the
-     * future, we might use these for priority but this is good enough for
-     * now until BIOS vendors actually mean something by using them.
+     * We have to find the source device (PCI interrupt link device).
      */
-    prsres = NULL;
-    for (i = prt->SourceIndex; prsres == NULL; i++) {
-       if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, i, &prsres))) {
-           device_printf(pcib, "_PRS lacks IRQ resource, routing failed\n");
-           goto out;
-       }
-       switch (prsres->Type) {
-       case ACPI_RESOURCE_TYPE_IRQ:
-           InterruptCount = prsres->Data.Irq.InterruptCount;
-           device_printf(pcib, "possible interrupts:");
-           for (i = 0; i < InterruptCount; i++)
-               kprintf("  %d", prsres->Data.Irq.Interrupts[i]);
-           kprintf("\n");
-           intr = prsres->Data.Irq.Interrupts[0];
-           break;
-       case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
-           InterruptCount = prsres->Data.ExtendedIrq.InterruptCount;
-           device_printf(pcib, "possible interrupts:");
-           for (i = 0; i < InterruptCount; i++)
-               kprintf("  %d", prsres->Data.ExtendedIrq.Interrupts[i]);
-           kprintf("\n");
-           intr = prsres->Data.ExtendedIrq.Interrupts[0];
-           break;
-       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
-           prsres = NULL;
-           continue;
-       default:
-           device_printf(pcib, "_PRS has invalid type %d\n", prsres->Type);
-           goto out;
-       }
-    }
-
-    /* There has to be at least one interrupt available. */
-    if (InterruptCount < 1) {
-       device_printf(pcib, "device has no interrupts\n");
+    if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
+       device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
+           prt->Source);
        goto out;
     }
+    interrupt = acpi_pci_link_route_interrupt(acpi_get_device(lnkdev),
+       prt->SourceIndex);
 
-    /*
-     * Pick an interrupt to use.  Note that a more scientific approach than
-     * just taking the first one available would be desirable.
-     *
-     * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI
-     * doesn't seem to offer a similar mechanism, so picking a "good"
-     * interrupt here is a difficult task.
-     *
-     * Build a resource buffer and pass it to AcpiSetCurrentResources to
-     * route the new interrupt.
-     */
-
-    /* This should never happen. */
-    if (crsbuf.Pointer != NULL)
-       AcpiOsFree(crsbuf.Pointer);
-
-    /* XXX Data.Irq and Data.ExtendedIrq are implicitly structure-copied. */
-    crsbuf.Pointer = NULL;
-    if (prsres->Type == ACPI_RESOURCE_TYPE_IRQ) {
-       resbuf.Type = ACPI_RESOURCE_TYPE_IRQ;
-       resbuf.Length = sizeof(ACPI_RESOURCE_IRQ);
-       resbuf.Data.Irq = prsres->Data.Irq;
-       resbuf.Data.Irq.InterruptCount = 1;
-       resbuf.Data.Irq.Interrupts[0] = intr;
-    } else {
-       resbuf.Type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
-       resbuf.Length = sizeof(ACPI_RESOURCE_EXTENDED_IRQ);
-       resbuf.Data.ExtendedIrq = prsres->Data.ExtendedIrq;
-       resbuf.Data.ExtendedIrq.InterruptCount = 1;
-       resbuf.Data.ExtendedIrq.Interrupts[0] = intr;
-    }
-    if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) {
-       device_printf(pcib, "buf append failed for interrupt %d via %s - %s\n",
-                     intr, acpi_name(lnkdev), AcpiFormatException(status));
-       goto out;
-    }
-    /* XXX Figure out how this is happening when the append succeeds. */
-    if (crsbuf.Pointer == NULL) {
-       device_printf(pcib, "_CRS buf NULL after append?\n");
-       goto out;
+    if (bootverbose && PCI_INTERRUPT_VALID(interrupt)) {
+       if (PCI_INTERRUPT_VALID(interrupt))
+           device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
+                pci_get_slot(dev), 'A' + pin, interrupt, acpi_name(lnkdev));
     }
-    if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) {
-       device_printf(pcib, "_SRS failed for interrupt %d via %s - %s\n",
-                     intr, acpi_name(lnkdev), AcpiFormatException(status));
-       goto out;
-    }
-    
-    /* Return the interrupt we just routed. */
-    device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n", 
-                 pci_get_slot(dev), 'A' + pin, intr, acpi_name(lnkdev));
-    interrupt = intr;
 
 out:
-    if (crsbuf.Pointer != NULL)
-       AcpiOsFree(crsbuf.Pointer);
-    if (prsbuf.Pointer != NULL)
-       AcpiOsFree(prsbuf.Pointer);
-    if (buf.Pointer != NULL)
-       AcpiOsFree(buf.Pointer);
-
-    /* XXX APIC_IO interrupt mapping? */
+    ACPI_SERIAL_END(pcib);
+
     return_VALUE (interrupt);
 }
index 13a5ec7..a7b707b 100644 (file)
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/dev/acpica/acpi_pcib_acpi.c,v 1.34 2004/05/30 20:08:23 phk Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pcib_acpi.c,v 1.5 2008/08/02 01:14:41 dillon Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pcib_acpi.c,v 1.55.8.1 2009/04/15 03:14:26 kensmith Exp $");
  */
+
+#include <sys/cdefs.h>
+
 #include "opt_acpi.h"
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/module.h>
+#include <sys/sysctl.h>
 
 #include "acpi.h"
-#include "accommon.h"
-#include "acpivar.h"
+#include <dev/acpica5/acpivar.h>
 
-#include <bus/pci/pci_cfgreg.h>
+#include <bus/pci/i386/pci_cfgreg.h>
 #include <bus/pci/pcivar.h>
 #include <bus/pci/pcib_private.h>
 #include "pcib_if.h"
 
-#include "acpi_pcibvar.h"
+#include <dev/acpica5/acpi_pcibvar.h>
 
 /* Hooks for the ACPI CA debugging infrastructure. */
 #define _COMPONENT     ACPI_BUS
@@ -52,6 +53,7 @@ ACPI_MODULE_NAME("PCI_ACPI")
 struct acpi_hpcib_softc {
     device_t           ap_dev;
     ACPI_HANDLE                ap_handle;
+    int                        ap_flags;
 
     int                        ap_segment;     /* analagous to Alpha 'hose' */
     int                        ap_bus;         /* bios-assigned bus number */
@@ -72,6 +74,14 @@ static void          acpi_pcib_write_config(device_t dev, int bus, int slot,
                            int func, int reg, uint32_t data, int bytes);
 static int             acpi_pcib_acpi_route_interrupt(device_t pcib,
                            device_t dev, int pin);
+#ifdef MSI
+static int             acpi_pcib_alloc_msi(device_t pcib, device_t dev,
+                           int count, int maxcount, int *irqs);
+static int             acpi_pcib_map_msi(device_t pcib, device_t dev,
+                           int irq, uint64_t *addr, uint32_t *data);
+static int             acpi_pcib_alloc_msix(device_t pcib, device_t dev,
+                           int *irq);
+#endif
 static struct resource *acpi_pcib_acpi_alloc_resource(device_t dev,
                            device_t child, int type, int *rid,
                            u_long start, u_long end, u_long count,
@@ -92,7 +102,7 @@ static device_method_t acpi_pcib_acpi_methods[] = {
     DEVMETHOD(bus_alloc_resource,      acpi_pcib_acpi_alloc_resource),
     DEVMETHOD(bus_release_resource,    bus_generic_release_resource),
     DEVMETHOD(bus_activate_resource,   bus_generic_activate_resource),
-    DEVMETHOD(bus_deactivate_resource,         bus_generic_deactivate_resource),
+    DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
     DEVMETHOD(bus_setup_intr,          bus_generic_setup_intr),
     DEVMETHOD(bus_teardown_intr,       bus_generic_teardown_intr),
 
@@ -101,34 +111,36 @@ static device_method_t acpi_pcib_acpi_methods[] = {
     DEVMETHOD(pcib_read_config,                acpi_pcib_read_config),
     DEVMETHOD(pcib_write_config,       acpi_pcib_write_config),
     DEVMETHOD(pcib_route_interrupt,    acpi_pcib_acpi_route_interrupt),
-
+#ifdef MSI
+    DEVMETHOD(pcib_alloc_msi,          acpi_pcib_alloc_msi),
+    DEVMETHOD(pcib_release_msi,                pcib_release_msi),
+    DEVMETHOD(pcib_alloc_msix,         acpi_pcib_alloc_msix),
+    DEVMETHOD(pcib_release_msix,       pcib_release_msix),
+    DEVMETHOD(pcib_map_msi,            acpi_pcib_map_msi),
+#endif
     {0, 0}
 };
 
-static driver_t acpi_pcib_acpi_driver = {
-    "pcib",
-    acpi_pcib_acpi_methods,
-    sizeof(struct acpi_hpcib_softc),
-};
-
 static devclass_t pcib_devclass;
 
+DEFINE_CLASS_0(pcib, acpi_pcib_acpi_driver, acpi_pcib_acpi_methods,
+    sizeof(struct acpi_hpcib_softc));
 DRIVER_MODULE(acpi_pcib, acpi, acpi_pcib_acpi_driver, pcib_devclass, 0, 0);
 MODULE_DEPEND(acpi_pcib, acpi, 1, 1, 1);
 
 static int
 acpi_pcib_acpi_probe(device_t dev)
 {
+    static char *pcib_ids[] = { "PNP0A03", NULL };
 
-    if (acpi_get_type(dev) == ACPI_TYPE_DEVICE &&
-       acpi_disabled("pci") &&
-       acpi_MatchHid(acpi_get_handle(dev), "PNP0A03")) {
-       if (pci_cfgregopen() == 0)
-               return (ENXIO);
-       device_set_desc(dev, "ACPI Host-PCI bridge");
-       return (0);
-    }
-    return (ENXIO);
+    if (acpi_disabled("pcib") ||
+       ACPI_ID_PROBE(device_get_parent(dev), dev, pcib_ids) == NULL)
+       return (ENXIO);
+
+    if (pci_cfgregopen() == 0)
+       return (ENXIO);
+    device_set_desc(dev, "ACPI Host-PCI bridge");
+    return (0);
 }
 
 static int
@@ -149,7 +161,7 @@ acpi_pcib_acpi_attach(device_t dev)
      * Get our base bus number by evaluating _BBN.
      * If this doesn't work, we assume we're bus number 0.
      *
-     * XXX note that it may also not exist in the case where we are 
+     * XXX note that it may also not exist in the case where we are
      *     meant to use a private configuration space mechanism for this bus,
      *     so we should dig out our resources and check to see if we have
      *     anything like that.  How do we do this?
@@ -191,8 +203,8 @@ acpi_pcib_acpi_attach(device_t dev)
                device_printf(dev, "couldn't find _ADR\n");
        } else {
            /* XXX: We assume bus 0. */
-           slot = addr >> 16;
-           func = addr & 0xffff;
+           slot = ACPI_ADR_PCI_SLOT(addr);
+           func = ACPI_ADR_PCI_FUNC(addr);
            if (bootverbose)
                device_printf(dev, "reading config registers from 0:%d:%d\n",
                    slot, func);
@@ -230,16 +242,14 @@ acpi_pcib_acpi_attach(device_t dev)
        /* If it's not found, assume 0. */
        sc->ap_segment = 0;
     }
-
     return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_bus));
 }
 
 static int
 acpi_pcib_acpi_resume(device_t dev)
 {
-    struct acpi_hpcib_softc    *sc = device_get_softc(dev);
 
-    return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_bus));
+    return (acpi_pcib_resume(dev));
 }
 
 /*
@@ -251,12 +261,18 @@ acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
     struct acpi_hpcib_softc    *sc = device_get_softc(dev);
 
     switch (which) {
+    case PCIB_IVAR_DOMAIN:
+       *result = 0;
+       return (0);
     case PCIB_IVAR_BUS:
        *result = sc->ap_bus;
        return (0);
     case ACPI_IVAR_HANDLE:
        *result = (uintptr_t)sc->ap_handle;
        return (0);
+    case ACPI_IVAR_FLAGS:
+       *result = (uintptr_t)sc->ap_flags;
+       return (0);
     }
     return (ENOENT);
 }
@@ -264,12 +280,20 @@ acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
 static int
 acpi_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
 {
-    struct acpi_hpcib_softc    *sc = device_get_softc(dev);
+    struct acpi_hpcib_softc    *sc = device_get_softc(dev);
 
     switch (which) {
+    case PCIB_IVAR_DOMAIN:
+       return (EINVAL);
     case PCIB_IVAR_BUS:
        sc->ap_bus = value;
        return (0);
+    case ACPI_IVAR_HANDLE:
+       sc->ap_handle = (ACPI_HANDLE)value;
+       return (0);
+    case ACPI_IVAR_FLAGS:
+       sc->ap_flags = (int)value;
+       return (0);
     }
     return (ENOENT);
 }
@@ -291,26 +315,61 @@ acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg,
 static int
 acpi_pcib_acpi_route_interrupt(device_t pcib, device_t dev, int pin)
 {
-    struct acpi_hpcib_softc *sc;
+    struct acpi_hpcib_softc *sc = device_get_softc(pcib);
 
-    /* Find the bridge softc. */
-    sc = device_get_softc(pcib);
     return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt));
 }
+#ifdef MSI
+static int
+acpi_pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount,
+    int *irqs)
+{
+       device_t bus;
+
+       bus = device_get_parent(pcib);
+       return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount,
+           irqs));
+}
+
+static int
+acpi_pcib_alloc_msix(device_t pcib, device_t dev, int *irq)
+{
+       device_t bus;
+
+       bus = device_get_parent(pcib);
+       return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq));
+}
+
+static int
+acpi_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
+    uint32_t *data)
+{
+       device_t bus;
+
+       bus = device_get_parent(pcib);
+       return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data));
+}
+#endif
+static u_long acpi_host_mem_start = 0x80000000;
+TUNABLE_INT("hw.acpi.host_mem_start", &acpi_host_mem_start);
 
 struct resource *
 acpi_pcib_acpi_alloc_resource(device_t dev, device_t child, int type, int *rid,
     u_long start, u_long end, u_long count, u_int flags)
 {
     /*
-     * If no memory preference is given, use upper 256MB slot most
+     * If no memory preference is given, use upper 32MB slot most
      * bioses use for their memory window.  Typically other bridges
      * before us get in the way to assert their preferences on memory.
      * Hardcoding like this sucks, so a more MD/MI way needs to be
-     * found to do it.
+     * found to do it.  This is typically only used on older laptops
+     * that don't have pci busses behind pci bridge, so assuming > 32MB
+     * is liekly OK.
      */
     if (type == SYS_RES_MEMORY && start == 0UL && end == ~0UL)
-       start = 0xf0000000;
+       start = acpi_host_mem_start;
+    if (type == SYS_RES_IOPORT && start == 0UL && end == ~0UL)
+       start = 0x1000;
     return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
        count, flags));
 }
index cafe57b..deaac6e 100644 (file)
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/dev/acpica/acpi_pcib_pci.c,v 1.9 2004/05/30 20:08:23 phk Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pcib_pci.c,v 1.5 2008/08/02 01:14:41 dillon Exp $
+ * __FBSDID("$FreeBSD: src/sys/dev/acpica/acpi_pcib_pci.c,v 1.17.8.1 2009/04/15 03:14:26 kensmith Exp $");
  */
 
+#include <sys/cdefs.h>
+
 #include "opt_acpi.h"
 
 #include <sys/param.h>
 #include <sys/module.h>
 
 #include "acpi.h"
-#include "accommon.h"
-#include "acpivar.h"
-#include "acpi_pcibvar.h"
+#include <dev/acpica5/acpivar.h>
+#include <dev/acpica5/acpi_pcibvar.h>
 
-#include <bus/pci/pci_cfgreg.h>
+#include <bus/pci/i386/pci_cfgreg.h>
 #include <bus/pci/pcivar.h>
 #include <bus/pci/pcireg.h>
 #include <bus/pci/pcib_private.h>
@@ -62,8 +61,6 @@ struct acpi_pcib_lookup_info {
     ACPI_HANDLE                handle;
 };
 
-static devclass_t pcib_devclass;
-
 static int             acpi_pcib_pci_probe(device_t bus);
 static int             acpi_pcib_pci_attach(device_t bus);
 static int             acpi_pcib_pci_resume(device_t bus);
@@ -84,7 +81,8 @@ static device_method_t acpi_pcib_pci_methods[] = {
     DEVMETHOD(bus_print_child,         bus_generic_print_child),
     DEVMETHOD(bus_read_ivar,           acpi_pcib_read_ivar),
     DEVMETHOD(bus_write_ivar,          pcib_write_ivar),
-    DEVMETHOD(bus_alloc_resource,      pcib_alloc_resource),
+   // DEVMETHOD(bus_alloc_resource,    pcib_alloc_resource),
+    DEVMETHOD(bus_alloc_resource,      bus_generic_alloc_resource),
     DEVMETHOD(bus_release_resource,    bus_generic_release_resource),
     DEVMETHOD(bus_activate_resource,   bus_generic_activate_resource),
     DEVMETHOD(bus_deactivate_resource,         bus_generic_deactivate_resource),
@@ -96,22 +94,26 @@ static device_method_t acpi_pcib_pci_methods[] = {
     DEVMETHOD(pcib_read_config,                pcib_read_config),
     DEVMETHOD(pcib_write_config,       pcib_write_config),
     DEVMETHOD(pcib_route_interrupt,    acpi_pcib_pci_route_interrupt),
-
+#ifdef MSI
+    DEVMETHOD(pcib_alloc_msi,          pcib_alloc_msi),
+    DEVMETHOD(pcib_release_msi,                pcib_release_msi),
+    DEVMETHOD(pcib_alloc_msix,         pcib_alloc_msix),
+    DEVMETHOD(pcib_release_msix,       pcib_release_msix),
+    DEVMETHOD(pcib_map_msi,            pcib_map_msi),
+#endif
     {0, 0}
 };
 
-static driver_t acpi_pcib_pci_driver = {
-    "pcib",
-    acpi_pcib_pci_methods,
-    sizeof(struct acpi_pcib_softc),
-};
-
+static devclass_t pcib_devclass;
+DEFINE_CLASS_0(pcib, acpi_pcib_pci_driver, acpi_pcib_pci_methods,
+    sizeof(struct acpi_pcib_softc));
 DRIVER_MODULE(acpi_pcib, pci, acpi_pcib_pci_driver, pcib_devclass, 0, 0);
 MODULE_DEPEND(acpi_pcib, acpi, 1, 1, 1);
 
 static int
 acpi_pcib_pci_probe(device_t dev)
 {
+
     if (pci_get_class(dev) != PCIC_BRIDGE ||
        pci_get_subclass(dev) != PCIS_BRIDGE_PCI ||
        acpi_disabled("pci"))
@@ -122,16 +124,14 @@ acpi_pcib_pci_probe(device_t dev)
        return (ENXIO);
 
     device_set_desc(dev, "ACPI PCI-PCI bridge");
-    return (-1000);
+    return (-100);
 }
 
 static int
 acpi_pcib_pci_attach(device_t dev)
 {
     struct acpi_pcib_softc *sc;
-
     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
     pcib_attach_common(dev);
     sc = device_get_softc(dev);
     sc->ap_handle = acpi_get_handle(dev);
@@ -141,9 +141,8 @@ acpi_pcib_pci_attach(device_t dev)
 static int
 acpi_pcib_pci_resume(device_t dev)
 {
-    struct acpi_pcib_softc *sc = device_get_softc(dev);
 
-    return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_pcibsc.secbus));
+    return (acpi_pcib_resume(dev));
 }
 
 static int
@@ -170,8 +169,10 @@ acpi_pcib_pci_route_interrupt(device_t pcib, device_t dev, int pin)
      * If we don't have a _PRT, fall back to the swizzle method
      * for routing interrupts.
      */
-    if (sc->ap_prt.Pointer == NULL)
+    if (sc->ap_prt.Pointer == NULL) {
+device_printf(pcib, "No _PRT found, routing with pci\n");
        return (pcib_route_interrupt(pcib, dev, pin));
+}
     else
        return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt));
 }
index add6947..bbbacd5 100644 (file)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/acpica/acpi_pcibvar.h,v 1.2 2002/10/05 02:01:02 iwasaki Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_pcibvar.h,v 1.1 2004/02/21 06:48:08 dillon Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_pcibvar.h,v 1.6.20.1 2009/04/15 03:14:26 kensmith Exp $
  */
 
 #ifndef        _ACPI_PCIBVAR_H_
 #define        _ACPI_PCIBVAR_H_
 
+#ifdef _KERNEL
+void   acpi_pci_link_add_reference(device_t dev, int index, device_t pcib,
+    int slot, int pin);
+int    acpi_pci_link_route_interrupt(device_t dev, int index);
 int    acpi_pcib_attach(device_t bus, ACPI_BUFFER *prt, int busno);
 int    acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
-    ACPI_BUFFER *ptrbuf);
-int    acpi_pcib_resume(device_t bus, ACPI_BUFFER *prt, int busno);
+    ACPI_BUFFER *prtbuf);
+int    acpi_pcib_resume(device_t dev);
+#endif /* _KERNEL */
 
-int    acpi_pci_link_config(device_t pcib, ACPI_BUFFER *prt, int busno);
-int    acpi_pci_link_resume(device_t pcib, ACPI_BUFFER *prt, int busno);
-
-#endif
+#endif /* !_ACPI_PCIBVAR_H_ */