xref: /freebsd/sys/dev/acpica/acpi_pcib_acpi.c (revision b4a052380f7a5238f23a8e977975ca1a2fb9645f)
115e32d5dSMike Smith /*-
215e32d5dSMike Smith  * Copyright (c) 2000 Michael Smith
315e32d5dSMike Smith  * Copyright (c) 2000 BSDi
415e32d5dSMike Smith  * All rights reserved.
515e32d5dSMike Smith  *
615e32d5dSMike Smith  * Redistribution and use in source and binary forms, with or without
715e32d5dSMike Smith  * modification, are permitted provided that the following conditions
815e32d5dSMike Smith  * are met:
915e32d5dSMike Smith  * 1. Redistributions of source code must retain the above copyright
1015e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer.
1115e32d5dSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1215e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer in the
1315e32d5dSMike Smith  *    documentation and/or other materials provided with the distribution.
1415e32d5dSMike Smith  *
1515e32d5dSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1615e32d5dSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1715e32d5dSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1815e32d5dSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1915e32d5dSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2015e32d5dSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2115e32d5dSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2215e32d5dSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2315e32d5dSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2415e32d5dSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2515e32d5dSMike Smith  * SUCH DAMAGE.
2615e32d5dSMike Smith  *
2715e32d5dSMike Smith  *	$FreeBSD$
2815e32d5dSMike Smith  */
2915e32d5dSMike Smith #include "opt_acpi.h"
3015e32d5dSMike Smith #include <sys/param.h>
3115e32d5dSMike Smith #include <sys/bus.h>
324fa387b6SMike Smith #include <sys/malloc.h>
334fa387b6SMike Smith #include <sys/kernel.h>
3415e32d5dSMike Smith 
3515e32d5dSMike Smith #include "acpi.h"
3615e32d5dSMike Smith 
3715e32d5dSMike Smith #include <dev/acpica/acpivar.h>
3815e32d5dSMike Smith 
3915e32d5dSMike Smith #include <machine/pci_cfgreg.h>
4015e32d5dSMike Smith #include <pci/pcivar.h>
4115e32d5dSMike Smith #include "pcib_if.h"
4215e32d5dSMike Smith 
430ae55423SMike Smith /*
440ae55423SMike Smith  * Hooks for the ACPI CA debugging infrastructure
450ae55423SMike Smith  */
46e71b6381SMike Smith #define _COMPONENT	ACPI_BUS
47c1b1f787SMike Smith ACPI_MODULE_NAME("PCI")
480ae55423SMike Smith 
4915e32d5dSMike Smith struct acpi_pcib_softc {
5015e32d5dSMike Smith     device_t		ap_dev;
5115e32d5dSMike Smith     ACPI_HANDLE		ap_handle;
5215e32d5dSMike Smith 
5315e32d5dSMike Smith     int			ap_segment;	/* analagous to Alpha 'hose' */
5415e32d5dSMike Smith     int			ap_bus;		/* bios-assigned bus number */
554fa387b6SMike Smith 
564fa387b6SMike Smith     ACPI_BUFFER		ap_prt;		/* interrupt routing table */
5715e32d5dSMike Smith };
5815e32d5dSMike Smith 
5915e32d5dSMike Smith static int		acpi_pcib_probe(device_t bus);
6015e32d5dSMike Smith static int		acpi_pcib_attach(device_t bus);
6115e32d5dSMike Smith static int		acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result);
6215e32d5dSMike Smith static int		acpi_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value);
6315e32d5dSMike Smith static int		acpi_pcib_maxslots(device_t dev);
6415e32d5dSMike Smith static u_int32_t	acpi_pcib_read_config(device_t dev, int bus, int slot, int func, int reg, int bytes);
6515e32d5dSMike Smith static void		acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg,
6615e32d5dSMike Smith 					       u_int32_t data, int bytes);
6739a8493aSMike Smith static int		acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin);
6815e32d5dSMike Smith 
6915e32d5dSMike Smith static device_method_t acpi_pcib_methods[] = {
7015e32d5dSMike Smith     /* Device interface */
7115e32d5dSMike Smith     DEVMETHOD(device_probe,		acpi_pcib_probe),
7215e32d5dSMike Smith     DEVMETHOD(device_attach,		acpi_pcib_attach),
7315e32d5dSMike Smith     DEVMETHOD(device_shutdown,		bus_generic_shutdown),
7415e32d5dSMike Smith     DEVMETHOD(device_suspend,		bus_generic_suspend),
7515e32d5dSMike Smith     DEVMETHOD(device_resume,		bus_generic_resume),
7615e32d5dSMike Smith 
7715e32d5dSMike Smith     /* Bus interface */
7815e32d5dSMike Smith     DEVMETHOD(bus_print_child,		bus_generic_print_child),
7915e32d5dSMike Smith     DEVMETHOD(bus_read_ivar,		acpi_pcib_read_ivar),
8015e32d5dSMike Smith     DEVMETHOD(bus_write_ivar,		acpi_pcib_write_ivar),
8115e32d5dSMike Smith     DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
8215e32d5dSMike Smith     DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
8315e32d5dSMike Smith     DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
8415e32d5dSMike Smith     DEVMETHOD(bus_deactivate_resource, 	bus_generic_deactivate_resource),
8515e32d5dSMike Smith     DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
8615e32d5dSMike Smith     DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
8715e32d5dSMike Smith 
8815e32d5dSMike Smith     /* pcib interface */
8915e32d5dSMike Smith     DEVMETHOD(pcib_maxslots,		acpi_pcib_maxslots),
9015e32d5dSMike Smith     DEVMETHOD(pcib_read_config,		acpi_pcib_read_config),
9115e32d5dSMike Smith     DEVMETHOD(pcib_write_config,	acpi_pcib_write_config),
9215e32d5dSMike Smith     DEVMETHOD(pcib_route_interrupt,	acpi_pcib_route_interrupt),
9315e32d5dSMike Smith 
9415e32d5dSMike Smith     {0, 0}
9515e32d5dSMike Smith };
9615e32d5dSMike Smith 
9715e32d5dSMike Smith static driver_t acpi_pcib_driver = {
9815e32d5dSMike Smith     "acpi_pcib",
9915e32d5dSMike Smith     acpi_pcib_methods,
10015e32d5dSMike Smith     sizeof(struct acpi_pcib_softc),
10115e32d5dSMike Smith };
10215e32d5dSMike Smith 
1033273b005SMike Smith static devclass_t acpi_pcib_devclass;
10415e32d5dSMike Smith DRIVER_MODULE(acpi_pcib, acpi, acpi_pcib_driver, acpi_pcib_devclass, 0, 0);
10515e32d5dSMike Smith 
10615e32d5dSMike Smith static int
10715e32d5dSMike Smith acpi_pcib_probe(device_t dev)
10815e32d5dSMike Smith {
10915e32d5dSMike Smith 
11015e32d5dSMike Smith     if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
1110ae55423SMike Smith 	!acpi_disabled("pci") &&
11215e32d5dSMike Smith 	acpi_MatchHid(dev, "PNP0A03")) {
11315e32d5dSMike Smith 
11415e32d5dSMike Smith 	/*
11515e32d5dSMike Smith 	 * Set device description
11615e32d5dSMike Smith 	 */
11715e32d5dSMike Smith 	device_set_desc(dev, "Host-PCI bridge");
11815e32d5dSMike Smith 	return(0);
11915e32d5dSMike Smith     }
12015e32d5dSMike Smith     return(ENXIO);
12115e32d5dSMike Smith }
12215e32d5dSMike Smith 
12315e32d5dSMike Smith static int
12415e32d5dSMike Smith acpi_pcib_attach(device_t dev)
12515e32d5dSMike Smith {
12615e32d5dSMike Smith     struct acpi_pcib_softc	*sc;
12715e32d5dSMike Smith     device_t			child;
12815e32d5dSMike Smith     ACPI_STATUS			status;
1290ae55423SMike Smith     int				result;
1300ae55423SMike Smith 
131b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
13215e32d5dSMike Smith 
13315e32d5dSMike Smith     sc = device_get_softc(dev);
13415e32d5dSMike Smith     sc->ap_dev = dev;
13515e32d5dSMike Smith     sc->ap_handle = acpi_get_handle(dev);
13615e32d5dSMike Smith 
13715e32d5dSMike Smith     /*
13815e32d5dSMike Smith      * Don't attach if we're not really there.
13915e32d5dSMike Smith      *
14015e32d5dSMike Smith      * XXX this isn't entirely correct, since we may be a PCI bus
14115e32d5dSMike Smith      * on a hot-plug docking station, etc.
14215e32d5dSMike Smith      */
14315e32d5dSMike Smith     if (!acpi_DeviceIsPresent(dev))
1440ae55423SMike Smith 	return_VALUE(ENXIO);
14515e32d5dSMike Smith 
14615e32d5dSMike Smith     /*
14715e32d5dSMike Smith      * Get our segment number by evaluating _SEG
14815e32d5dSMike Smith      * It's OK for this to not exist.
14915e32d5dSMike Smith      */
150c1b1f787SMike Smith     if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->ap_handle, "_SEG", &sc->ap_segment))) {
15115e32d5dSMike Smith 	if (status != AE_NOT_FOUND) {
152bfae45aaSMike Smith 	    device_printf(dev, "could not evaluate _SEG - %s\n", AcpiFormatException(status));
1530ae55423SMike Smith 	    return_VALUE(ENXIO);
15415e32d5dSMike Smith 	}
15515e32d5dSMike Smith 	/* if it's not found, assume 0 */
15615e32d5dSMike Smith 	sc->ap_segment = 0;
15715e32d5dSMike Smith     }
15815e32d5dSMike Smith 
15915e32d5dSMike Smith     /*
160c1b1f787SMike Smith      * Get our base bus number by evaluating _BBN.  If that doesn't work, try _ADR.
161c1b1f787SMike Smith      * If this doesn't work, we assume we're bus number 0.
16215e32d5dSMike Smith      *
16315e32d5dSMike Smith      * XXX note that it may also not exist in the case where we are
16415e32d5dSMike Smith      *     meant to use a private configuration space mechanism for this bus,
16515e32d5dSMike Smith      *     so we should dig out our resources and check to see if we have
16615e32d5dSMike Smith      *     anything like that.  How do we do this?
167042283a6SMike Smith      * XXX If we have the requisite information, and if we don't think the
168042283a6SMike Smith      *     default PCI configuration space handlers can deal with this bus,
169042283a6SMike Smith      *     we should attach our own handler.
170042283a6SMike Smith      * XXX invoke _REG on this for the PCI config space address space?
17115e32d5dSMike Smith      */
172c1b1f787SMike Smith     if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->ap_handle, "_BBN", &sc->ap_bus))) {
17315e32d5dSMike Smith 	if (status != AE_NOT_FOUND) {
174bfae45aaSMike Smith 	    device_printf(dev, "could not evaluate _BBN - %s\n", AcpiFormatException(status));
1750ae55423SMike Smith 	    return_VALUE(ENXIO);
17615e32d5dSMike Smith 	}
177c1b1f787SMike Smith 	if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->ap_handle, "_ADR", &sc->ap_bus))) {
178c1b1f787SMike Smith 	    if (status != AE_NOT_FOUND) {
179c1b1f787SMike Smith 		device_printf(dev, "could not evaluate _ADR - %s\n", AcpiFormatException(status));
180c1b1f787SMike Smith 		return_VALUE(ENXIO);
181c1b1f787SMike Smith 	    }
182c1b1f787SMike Smith 	} else {
18315e32d5dSMike Smith 	    /* if it's not found, assume 0 */
18415e32d5dSMike Smith 	    sc->ap_bus = 0;
18515e32d5dSMike Smith 	}
186c1b1f787SMike Smith     }
18715e32d5dSMike Smith 
18815e32d5dSMike Smith     /*
189c1b1f787SMike Smith      * Make sure that this bus hasn't already been found.
190042283a6SMike Smith      */
191c1b1f787SMike Smith     if (devclass_get_device(devclass_find("pci"), sc->ap_bus) != NULL) {
192c1b1f787SMike Smith 	device_printf(dev, "we have duplicate bus number %d - not probing bus\n", sc->ap_bus);
1930ae55423SMike Smith 	return_VALUE(0);
194c1b1f787SMike Smith     }
195042283a6SMike Smith 
196042283a6SMike Smith     /*
197c1b1f787SMike Smith      * Get the PCI interrupt routing table for this bus.
1984fa387b6SMike Smith      */
199c1b1f787SMike Smith     sc->ap_prt.Length = ACPI_ALLOCATE_BUFFER;
200c1b1f787SMike Smith     if (ACPI_FAILURE(status = AcpiGetIrqRoutingTable(sc->ap_handle, &sc->ap_prt))) {
201bfae45aaSMike Smith 	device_printf(dev, "could not get PCI interrupt routing table - %s\n", AcpiFormatException(status));
2024fa387b6SMike Smith 	/* this is not an error, but it may reduce functionality */
2034fa387b6SMike Smith     }
2044fa387b6SMike Smith 
2054fa387b6SMike Smith     /*
206042283a6SMike Smith      * Attach the PCI bus proper.
20715e32d5dSMike Smith      */
20815e32d5dSMike Smith     if ((child = device_add_child(dev, "pci", sc->ap_bus)) == NULL) {
20915e32d5dSMike Smith 	device_printf(device_get_parent(dev), "couldn't attach pci bus");
2100ae55423SMike Smith 	return_VALUE(ENXIO);
21115e32d5dSMike Smith     }
21215e32d5dSMike Smith 
21315e32d5dSMike Smith     /*
2143cd59d7bSScott Long      * Now go scan the bus.
2153cd59d7bSScott Long      *
2163cd59d7bSScott Long      * XXX It would be nice to defer this and count on the nexus getting it
2173cd59d7bSScott Long      * after the first pass, but this does not seem to be reliable.
21815e32d5dSMike Smith      */
2190ae55423SMike Smith     result = bus_generic_attach(dev);
2204fa387b6SMike Smith 
2214fa387b6SMike Smith     /*
222c1b1f787SMike Smith      * Now that we have established the device tree, we need to scan our children
223c1b1f787SMike Smith      * and hook them up with their corresponding device nodes.
224c1b1f787SMike Smith      *
225c1b1f787SMike Smith      * This is not trivial.
226c1b1f787SMike Smith      */
227c1b1f787SMike Smith 
228c1b1f787SMike Smith     /*
2294fa387b6SMike Smith      * XXX cross-reference our children to attached devices on the child bus
2304fa387b6SMike Smith      *     via _ADR, so we can provide power management.
2314fa387b6SMike Smith      */
2324fa387b6SMike Smith     /* XXX implement */
2334fa387b6SMike Smith 
2340ae55423SMike Smith     return_VALUE(result);
23515e32d5dSMike Smith }
23615e32d5dSMike Smith 
2374fa387b6SMike Smith /*
2384fa387b6SMike Smith  * ACPI doesn't tell us how many slots there are, so use the standard
2394fa387b6SMike Smith  * maximum.
2404fa387b6SMike Smith  */
24115e32d5dSMike Smith static int
24215e32d5dSMike Smith acpi_pcib_maxslots(device_t dev)
24315e32d5dSMike Smith {
2444fa387b6SMike Smith     return(PCI_SLOTMAX);
24515e32d5dSMike Smith }
24615e32d5dSMike Smith 
2474fa387b6SMike Smith /*
2484fa387b6SMike Smith  * Support for standard PCI bridge ivars.
2494fa387b6SMike Smith  */
25015e32d5dSMike Smith static int
25115e32d5dSMike Smith acpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
25215e32d5dSMike Smith {
25315e32d5dSMike Smith     struct acpi_pcib_softc	*sc = device_get_softc(dev);
25415e32d5dSMike Smith 
25515e32d5dSMike Smith     switch (which) {
25615e32d5dSMike Smith     case  PCIB_IVAR_BUS:
25715e32d5dSMike Smith 	*result = sc->ap_bus;
25815e32d5dSMike Smith 	return(0);
25915e32d5dSMike Smith     }
26015e32d5dSMike Smith     return(ENOENT);
26115e32d5dSMike Smith }
26215e32d5dSMike Smith 
26315e32d5dSMike Smith static int
26415e32d5dSMike Smith acpi_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
26515e32d5dSMike Smith {
26615e32d5dSMike Smith     struct acpi_pcib_softc	*sc = device_get_softc(dev);
26715e32d5dSMike Smith 
26815e32d5dSMike Smith     switch (which) {
26915e32d5dSMike Smith     case  PCIB_IVAR_BUS:
27015e32d5dSMike Smith 	sc->ap_bus = value;
27115e32d5dSMike Smith 	return(0);
27215e32d5dSMike Smith     }
27315e32d5dSMike Smith     return(ENOENT);
27415e32d5dSMike Smith }
27515e32d5dSMike Smith 
27615e32d5dSMike Smith static u_int32_t
27715e32d5dSMike Smith acpi_pcib_read_config(device_t dev, int bus, int slot, int func, int reg, int bytes)
27815e32d5dSMike Smith {
27915e32d5dSMike Smith     return(pci_cfgregread(bus, slot, func, reg, bytes));
28015e32d5dSMike Smith }
28115e32d5dSMike Smith 
28215e32d5dSMike Smith static void
28315e32d5dSMike Smith acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg, u_int32_t data, int bytes)
28415e32d5dSMike Smith {
28515e32d5dSMike Smith     pci_cfgregwrite(bus, slot, func, reg, data, bytes);
28615e32d5dSMike Smith }
28715e32d5dSMike Smith 
2884fa387b6SMike Smith /*
2894fa387b6SMike Smith  * Route an interrupt for a child of the bridge.
2904fa387b6SMike Smith  *
2914fa387b6SMike Smith  * XXX clean up error messages
2924fa387b6SMike Smith  *
2934fa387b6SMike Smith  * XXX this function is somewhat bulky
2944fa387b6SMike Smith  */
29515e32d5dSMike Smith static int
29639a8493aSMike Smith acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
29715e32d5dSMike Smith {
2984fa387b6SMike Smith     struct acpi_pcib_softc	*sc;
299c1b1f787SMike Smith     ACPI_PCI_ROUTING_TABLE	*prt;
3004fa387b6SMike Smith     ACPI_HANDLE			lnkdev;
3014fa387b6SMike Smith     ACPI_BUFFER			crsbuf, prsbuf;
302f16527bbSMike Smith     ACPI_RESOURCE		*crsres, *prsres, resbuf;
3034fa387b6SMike Smith     ACPI_DEVICE_INFO		devinfo;
3044fa387b6SMike Smith     ACPI_STATUS			status;
3054fa387b6SMike Smith     u_int8_t			*prtp;
3064fa387b6SMike Smith     device_t			*devlist;
3074fa387b6SMike Smith     int				devcount;
3084fa387b6SMike Smith     int				bus;
3094fa387b6SMike Smith     int				interrupt;
3104fa387b6SMike Smith     int				i;
311555143d0SPeter Wemm     uintptr_t			up;
3124fa387b6SMike Smith 
313b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
3144c1cdee6SMike Smith 
3154fa387b6SMike Smith     crsbuf.Pointer = NULL;
3164fa387b6SMike Smith     prsbuf.Pointer = NULL;
3174fa387b6SMike Smith     devlist = NULL;
3184fa387b6SMike Smith     interrupt = 255;
3194fa387b6SMike Smith 
3204fa387b6SMike Smith     /* ACPI numbers pins 0-3, not 1-4 like the BIOS */
3214fa387b6SMike Smith     pin--;
3224fa387b6SMike Smith 
3234fa387b6SMike Smith     /* find the bridge softc */
3244fa387b6SMike Smith     if (devclass_get_devices(acpi_pcib_devclass, &devlist, &devcount))
3254fa387b6SMike Smith 	goto out;
326555143d0SPeter Wemm     BUS_READ_IVAR(pcib, pcib, PCIB_IVAR_BUS, &up);
327555143d0SPeter Wemm     bus = up;
3284fa387b6SMike Smith     sc = NULL;
3294fa387b6SMike Smith     for (i = 0; i < devcount; i++) {
3304fa387b6SMike Smith 	sc = device_get_softc(*(devlist + i));
3314fa387b6SMike Smith 	if (sc->ap_bus == bus)
3324fa387b6SMike Smith 	    break;
3334fa387b6SMike Smith 	sc = NULL;
33415e32d5dSMike Smith     }
3354fa387b6SMike Smith     if (sc == NULL)			/* not one of ours */
3364fa387b6SMike Smith 	goto out;
3374fa387b6SMike Smith     prtp = sc->ap_prt.Pointer;
3384fa387b6SMike Smith     if (prtp == NULL)			/* didn't get routing table */
3394fa387b6SMike Smith 	goto out;
3404fa387b6SMike Smith 
3414fa387b6SMike Smith     /* scan the table looking for this device */
3424fa387b6SMike Smith     for (;;) {
343c1b1f787SMike Smith 	prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
3444fa387b6SMike Smith 
3454fa387b6SMike Smith 	if (prt->Length == 0)		/* end of table */
3464fa387b6SMike Smith 	    goto out;
3474fa387b6SMike Smith 
3484fa387b6SMike Smith 	/*
3494fa387b6SMike Smith 	 * Compare the slot number (high word of Address) and pin number
3504fa387b6SMike Smith 	 * (note that ACPI uses 0 for INTA) to check for a match.
3514fa387b6SMike Smith 	 *
3524fa387b6SMike Smith 	 * Note that the low word of the Address field (function number)
3534fa387b6SMike Smith 	 * is required by the specification to be 0xffff.  We don't risk
3544fa387b6SMike Smith 	 * checking it here.
3554fa387b6SMike Smith 	 */
3564fa387b6SMike Smith 	if ((((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev)) &&
3574fa387b6SMike Smith 	    (prt->Pin == pin)) {
358a067d210SDoug Rabson 	    if (bootverbose)
3594fa387b6SMike Smith 		device_printf(sc->ap_dev, "matched entry for %d.%d.INT%c (source %s)\n",
3604fa387b6SMike Smith 			      pci_get_bus(dev), pci_get_slot(dev), 'A' + pin, prt->Source);
3614fa387b6SMike Smith 	    break;
3624fa387b6SMike Smith 	}
3634fa387b6SMike Smith 
3644fa387b6SMike Smith 	/* skip to next entry */
3654fa387b6SMike Smith 	prtp += prt->Length;
3664fa387b6SMike Smith     }
3674fa387b6SMike Smith 
3684fa387b6SMike Smith     /*
3694fa387b6SMike Smith      * If source is empty/NULL, the source index is the global IRQ number.
3704fa387b6SMike Smith      */
3714fa387b6SMike Smith     if ((prt->Source == NULL) || (prt->Source[0] == '\0')) {
372a067d210SDoug Rabson 	if (bootverbose)
3734fa387b6SMike Smith 	    device_printf(sc->ap_dev, "device is hardwired to IRQ %d\n", prt->SourceIndex);
3744fa387b6SMike Smith 	interrupt = prt->SourceIndex;
3754fa387b6SMike Smith 	goto out;
3764fa387b6SMike Smith     }
3774fa387b6SMike Smith 
3784fa387b6SMike Smith     /*
3794fa387b6SMike Smith      * We have to find the source device (PCI interrupt link device)
3804fa387b6SMike Smith      */
3814fa387b6SMike Smith     if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
3824fa387b6SMike Smith 	device_printf(sc->ap_dev, "couldn't find PCI interrupt link device %s\n", prt->Source);
3834fa387b6SMike Smith 	goto out;
3844fa387b6SMike Smith     }
3854fa387b6SMike Smith 
3864fa387b6SMike Smith     /*
3874fa387b6SMike Smith      * Verify that this is a PCI link device, and that it's present.
3884fa387b6SMike Smith      */
3894fa387b6SMike Smith     if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &devinfo))) {
3904fa387b6SMike Smith 	device_printf(sc->ap_dev, "couldn't validate PCI interrupt link device %s\n", prt->Source);
3914fa387b6SMike Smith 	goto out;
3924fa387b6SMike Smith     }
3934fa387b6SMike Smith     if (!(devinfo.Valid & ACPI_VALID_HID) || strcmp("PNP0C0F", devinfo.HardwareId)) {
3944fa387b6SMike Smith 	device_printf(sc->ap_dev, "PCI interrupt link device %s has wrong _HID (%s)\n",
3954fa387b6SMike Smith 		      prt->Source, devinfo.HardwareId);
3964fa387b6SMike Smith 	goto out;
3974fa387b6SMike Smith     }
39843896e91SMike Smith     if (!acpi_DeviceIsPresent(sc->ap_dev)) {
39943896e91SMike Smith 	device_printf(sc->ap_dev, "PCI interrupt link device %s not present\n",
40043896e91SMike Smith 		      prt->Source);
4014fa387b6SMike Smith 	goto out;
4024fa387b6SMike Smith     }
4034fa387b6SMike Smith 
4044fa387b6SMike Smith     /*
4054fa387b6SMike Smith      * Get the current and possible resources for the interrupt link device.
4064fa387b6SMike Smith      */
407c1b1f787SMike Smith     crsbuf.Length = ACPI_ALLOCATE_BUFFER;
408c1b1f787SMike Smith     if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) {
4094fa387b6SMike Smith 	device_printf(sc->ap_dev, "couldn't get PCI interrupt link device _CRS data - %s\n",
410bfae45aaSMike Smith 		      AcpiFormatException(status));
4114fa387b6SMike Smith 	goto out;	/* this is fatal */
4124fa387b6SMike Smith     }
413c1b1f787SMike Smith     prsbuf.Length = ACPI_ALLOCATE_BUFFER;
4140a702a9bSMike Smith     if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) {
4154fa387b6SMike Smith 	device_printf(sc->ap_dev, "couldn't get PCI interrupt link device _PRS data - %s\n",
416bfae45aaSMike Smith 		      AcpiFormatException(status));
4174fa387b6SMike Smith 	/* this is not fatal, since it may be hardwired */
4184fa387b6SMike Smith     }
4194c1cdee6SMike Smith     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %d bytes for %s._CRS\n", crsbuf.Length, acpi_name(lnkdev)));
4204c1cdee6SMike Smith     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %d bytes for %s._PRS\n", prsbuf.Length, acpi_name(lnkdev)));
4214fa387b6SMike Smith 
4224fa387b6SMike Smith     /*
4234fa387b6SMike Smith      * The interrupt may already be routed, so check _CRS first.  We don't check the
4244fa387b6SMike Smith      * 'decoding' bit in the _STA result, since there's nothing in the spec that
4254fa387b6SMike Smith      * mandates it be set, however some BIOS' will set it if the decode is active.
4264fa387b6SMike Smith      *
4274fa387b6SMike Smith      * The Source Index points to the particular resource entry we're interested in.
4284fa387b6SMike Smith      */
429f16527bbSMike Smith     if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex, &crsres))) {
4304fa387b6SMike Smith 	device_printf(sc->ap_dev, "_CRS buffer corrupt, cannot route interrupt\n");
4314fa387b6SMike Smith 	goto out;
4324fa387b6SMike Smith     }
4334fa387b6SMike Smith 
4344fa387b6SMike Smith     /* type-check the resource we've got */
4354fa387b6SMike Smith     if (crsres->Id != ACPI_RSTYPE_IRQ) {    /* XXX ACPI_RSTYPE_EXT_IRQ */
4364fa387b6SMike Smith 	device_printf(sc->ap_dev, "_CRS resource entry has unsupported type %d\n", crsres->Id);
4374fa387b6SMike Smith 	goto out;
4384fa387b6SMike Smith     }
4394fa387b6SMike Smith 
4404fa387b6SMike Smith     /* if there's more than one interrupt, we are confused */
4414fa387b6SMike Smith     if (crsres->Data.Irq.NumberOfInterrupts > 1) {
4424fa387b6SMike Smith 	device_printf(sc->ap_dev, "device has too many interrupts (%d)\n", crsres->Data.Irq.NumberOfInterrupts);
4434fa387b6SMike Smith 	goto out;
4444fa387b6SMike Smith     }
4454fa387b6SMike Smith 
4464fa387b6SMike Smith     /*
4474fa387b6SMike Smith      * If there's only one interrupt, and it's not zero, then we're already routed.
4484fa387b6SMike Smith      *
4494fa387b6SMike Smith      * Note that we could also check the 'decoding' bit in _STA, but can't depend on
4504fa387b6SMike Smith      * it since it's not part of the spec.
4514fa387b6SMike Smith      *
4524fa387b6SMike Smith      * XXX check ASL examples to see if this is an acceptable set of tests
4534fa387b6SMike Smith      */
4544fa387b6SMike Smith     if ((crsres->Data.Irq.NumberOfInterrupts == 1) && (crsres->Data.Irq.Interrupts[0] != 0)) {
4554fa387b6SMike Smith 	device_printf(sc->ap_dev, "device is routed to IRQ %d\n", crsres->Data.Irq.Interrupts[0]);
4564fa387b6SMike Smith 	interrupt = crsres->Data.Irq.Interrupts[0];
4574fa387b6SMike Smith 	goto out;
4584fa387b6SMike Smith     }
4594fa387b6SMike Smith 
4604fa387b6SMike Smith     /*
4614fa387b6SMike Smith      * There isn't an interrupt, so we have to look at _PRS to get one.
4624fa387b6SMike Smith      * Get the set of allowed interrupts from the _PRS resource indexed by SourceIndex.
4634fa387b6SMike Smith      */
4644fa387b6SMike Smith     if (prsbuf.Pointer == NULL) {
4654fa387b6SMike Smith 	device_printf(sc->ap_dev, "device has no routed interrupt and no _PRS on PCI interrupt link device\n");
4664fa387b6SMike Smith 	goto out;
4674fa387b6SMike Smith     }
468f16527bbSMike Smith     if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, prt->SourceIndex, &prsres))) {
4694fa387b6SMike Smith 	device_printf(sc->ap_dev, "_PRS buffer corrupt, cannot route interrupt\n");
4704fa387b6SMike Smith 	goto out;
4714fa387b6SMike Smith     }
4724fa387b6SMike Smith 
4734fa387b6SMike Smith     /* type-check the resource we've got */
4744fa387b6SMike Smith     if (prsres->Id != ACPI_RSTYPE_IRQ) {    /* XXX ACPI_RSTYPE_EXT_IRQ */
4754fa387b6SMike Smith 	device_printf(sc->ap_dev, "_PRS resource entry has unsupported type %d\n", prsres->Id);
4764fa387b6SMike Smith 	goto out;
4774fa387b6SMike Smith     }
4784fa387b6SMike Smith 
4794fa387b6SMike Smith     /* there has to be at least one interrupt available */
4804fa387b6SMike Smith     if (prsres->Data.Irq.NumberOfInterrupts < 1) {
4814fa387b6SMike Smith 	device_printf(sc->ap_dev, "device has no interrupts\n");
4824fa387b6SMike Smith 	goto out;
4834fa387b6SMike Smith     }
4844fa387b6SMike Smith 
4854fa387b6SMike Smith     /*
4864fa387b6SMike Smith      * Pick an interrupt to use.  Note that a more scientific approach than just
4874fa387b6SMike Smith      * taking the first one available would be desirable.
4884fa387b6SMike Smith      *
4894fa387b6SMike Smith      * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI doesn't
4904fa387b6SMike Smith      * seem to offer a similar mechanism, so picking a "good" interrupt here is a
4914fa387b6SMike Smith      * difficult task.
4924fa387b6SMike Smith      *
493f16527bbSMike Smith      * Build a resource buffer and pass it to AcpiSetCurrentResources to route the
494f16527bbSMike Smith      * new interrupt.
4954fa387b6SMike Smith      */
4964fa387b6SMike Smith     device_printf(sc->ap_dev, "possible interrupts:");
4974fa387b6SMike Smith     for (i = 0; i < prsres->Data.Irq.NumberOfInterrupts; i++)
4984fa387b6SMike Smith 	printf("  %d", prsres->Data.Irq.Interrupts[i]);
4994fa387b6SMike Smith     printf("\n");
500f16527bbSMike Smith 
501f16527bbSMike Smith     if (crsbuf.Pointer != NULL)			/* should never happen */
502f16527bbSMike Smith 	AcpiOsFree(crsbuf.Pointer);
503f16527bbSMike Smith     crsbuf.Pointer = NULL;
504f16527bbSMike Smith     resbuf.Id = ACPI_RSTYPE_IRQ;
505c1b1f787SMike Smith     resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
506f16527bbSMike Smith     resbuf.Data.Irq = prsres->Data.Irq;		/* structure copy other fields */
507f16527bbSMike Smith     resbuf.Data.Irq.NumberOfInterrupts = 1;
508f16527bbSMike Smith     resbuf.Data.Irq.Interrupts[0] = prsres->Data.Irq.Interrupts[0];	/* just take first... */
509f16527bbSMike Smith     if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) {
510f16527bbSMike Smith 	device_printf(sc->ap_dev, "couldn't route interrupt %d via %s, interupt resource build failed - %s\n",
511f16527bbSMike Smith 		      prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
512f16527bbSMike Smith 	goto out;
513f16527bbSMike Smith     }
514d93b034fSMike Smith     if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) {
5154fa387b6SMike Smith 	device_printf(sc->ap_dev, "couldn't route interrupt %d via %s - %s\n",
516bfae45aaSMike Smith 		      prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
5174fa387b6SMike Smith 	goto out;
5184fa387b6SMike Smith     }
5194fa387b6SMike Smith 
5204fa387b6SMike Smith     /* successful, return the interrupt we just routed */
5214fa387b6SMike Smith     device_printf(sc->ap_dev, "routed interrupt %d via %s\n",
5224fa387b6SMike Smith 		  prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev));
5234fa387b6SMike Smith     interrupt = prsres->Data.Irq.Interrupts[0];
5244fa387b6SMike Smith 
5254fa387b6SMike Smith  out:
5264fa387b6SMike Smith     if (devlist != NULL)
5274fa387b6SMike Smith 	free(devlist, M_TEMP);
5284fa387b6SMike Smith     if (crsbuf.Pointer != NULL)
5294fa387b6SMike Smith 	AcpiOsFree(crsbuf.Pointer);
5304fa387b6SMike Smith     if (prsbuf.Pointer != NULL)
5314fa387b6SMike Smith 	AcpiOsFree(prsbuf.Pointer);
5324fa387b6SMike Smith 
5334fa387b6SMike Smith     /* XXX APIC_IO interrupt mapping? */
5344c1cdee6SMike Smith     return_VALUE(interrupt);
5354fa387b6SMike Smith }
5364fa387b6SMike Smith 
537