From e389806b75d7c3b7959aa951f05b7cf3b29e7961 Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Wed, 14 Oct 2009 23:11:34 +0400 Subject: [PATCH] Bring in acpi_pci_* from FreeBSD 7.2 --- sys/dev/acpica5/acpi_pci.c | 313 ++++-- sys/dev/acpica5/acpi_pci_link.c | 1780 ++++++++++++++++-------------- sys/dev/acpica5/acpi_pcib.c | 471 ++++---- sys/dev/acpica5/acpi_pcib_acpi.c | 133 ++- sys/dev/acpica5/acpi_pcib_pci.c | 47 +- sys/dev/acpica5/acpi_pcibvar.h | 17 +- 6 files changed, 1488 insertions(+), 1273 deletions(-) diff --git a/sys/dev/acpica5/acpi_pci.c b/sys/dev/acpica5/acpi_pci.c index 5f8e16269b..895d6e05c8 100644 --- a/sys/dev/acpica5/acpi_pci.c +++ b/sys/dev/acpica5/acpi_pci.c @@ -1,6 +1,6 @@ -/* - * Copyright (c) 1997, Stefan Esser - * Copyright (c) 2000, Michael Smith +/*- + * Copyright (c) 1997, Stefan Esser + * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * All rights reserved. * @@ -24,12 +24,10 @@ * 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 #include #include @@ -39,7 +37,6 @@ #include #include "acpi.h" -#include "accommon.h" #include "acpivar.h" #include @@ -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,15 +139,35 @@ 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)); +} diff --git a/sys/dev/acpica5/acpi_pci_link.c b/sys/dev/acpica5/acpi_pci_link.c index 42e0ca800f..b40afdc540 100644 --- a/sys/dev/acpica5/acpi_pci_link.c +++ b/sys/dev/acpica5/acpi_pci_link.c @@ -1,5 +1,5 @@ -/*- - * Copyright (c) 2002 Mitsuru IWASAKI +/* + * Copyright (c) 2002 Mitsuru IWASAKI * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,1003 +22,1109 @@ * 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 + #include "opt_acpi.h" #include -#include #include +#include +#include +#include +#include #include "acpi.h" -#include "accommon.h" #include #include +#include +#include +#include +#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); diff --git a/sys/dev/acpica5/acpi_pcib.c b/sys/dev/acpica5/acpi_pcib.c index e5305d676f..acab302186 100644 --- a/sys/dev/acpica5/acpi_pcib.c +++ b/sys/dev/acpica5/acpi_pcib.c @@ -23,29 +23,111 @@ * 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 + #include "opt_acpi.h" #include #include #include #include +#include #include "acpi.h" -#include "accommon.h" -#include "acpivar.h" -#include "acpi_pcibvar.h" +#include +#include #include +#include #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); } diff --git a/sys/dev/acpica5/acpi_pcib_acpi.c b/sys/dev/acpica5/acpi_pcib_acpi.c index 13a5ec7fa8..a7b707b76f 100644 --- a/sys/dev/acpica5/acpi_pcib_acpi.c +++ b/sys/dev/acpica5/acpi_pcib_acpi.c @@ -23,27 +23,28 @@ * 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 + #include "opt_acpi.h" #include #include #include #include #include +#include #include "acpi.h" -#include "accommon.h" -#include "acpivar.h" +#include -#include +#include #include #include #include "pcib_if.h" -#include "acpi_pcibvar.h" +#include /* 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)); } diff --git a/sys/dev/acpica5/acpi_pcib_pci.c b/sys/dev/acpica5/acpi_pcib_pci.c index cafe57bd61..deaac6e2a3 100644 --- a/sys/dev/acpica5/acpi_pcib_pci.c +++ b/sys/dev/acpica5/acpi_pcib_pci.c @@ -23,11 +23,11 @@ * 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 + #include "opt_acpi.h" #include @@ -37,11 +37,10 @@ #include #include "acpi.h" -#include "accommon.h" -#include "acpivar.h" -#include "acpi_pcibvar.h" +#include +#include -#include +#include #include #include #include @@ -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)); } diff --git a/sys/dev/acpica5/acpi_pcibvar.h b/sys/dev/acpica5/acpi_pcibvar.h index add69471db..bbbacd596b 100644 --- a/sys/dev/acpica5/acpi_pcibvar.h +++ b/sys/dev/acpica5/acpi_pcibvar.h @@ -24,19 +24,20 @@ * 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_ */ -- 2.41.0