xref: /freebsd/sys/dev/acpica/acpi_pcib.c (revision 5e1ba6d4aed84982dba427d83cc632fb14f66600)
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  */
27aad970f1SDavid E. O'Brien 
28aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
29aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
30aad970f1SDavid E. O'Brien 
3115e32d5dSMike Smith #include "opt_acpi.h"
3215e32d5dSMike Smith #include <sys/param.h>
3315e32d5dSMike Smith #include <sys/bus.h>
344fa387b6SMike Smith #include <sys/malloc.h>
354fa387b6SMike Smith #include <sys/kernel.h>
3615e32d5dSMike Smith 
3715e32d5dSMike Smith #include "acpi.h"
3815e32d5dSMike Smith #include <dev/acpica/acpivar.h>
392ccfc932SJohn Baldwin #include <dev/acpica/acpi_pcibvar.h>
4015e32d5dSMike Smith 
41cace7a2aSWarner Losh #include <dev/pci/pcivar.h>
4215e32d5dSMike Smith #include "pcib_if.h"
4315e32d5dSMike Smith 
443304735dSNate Lawson /* Hooks for the ACPI CA debugging infrastructure. */
45e71b6381SMike Smith #define _COMPONENT	ACPI_BUS
46c1b1f787SMike Smith ACPI_MODULE_NAME("PCI")
470ae55423SMike Smith 
48bbf7c27aSNate Lawson ACPI_SERIAL_DECL(pcib, "ACPI PCI bus methods");
49bbf7c27aSNate Lawson 
50bbf7c27aSNate Lawson /*
51bbf7c27aSNate Lawson  * For locking, we assume the caller is not concurrent since this is
52bbf7c27aSNate Lawson  * triggered by newbus methods.
53bbf7c27aSNate Lawson  */
545e1ba6d4SJohn Baldwin 
555e1ba6d4SJohn Baldwin struct prt_lookup_request {
565e1ba6d4SJohn Baldwin     ACPI_PCI_ROUTING_TABLE *pr_entry;
575e1ba6d4SJohn Baldwin     u_int	pr_pin;
585e1ba6d4SJohn Baldwin     u_int	pr_slot;
595e1ba6d4SJohn Baldwin };
605e1ba6d4SJohn Baldwin 
615e1ba6d4SJohn Baldwin typedef	void prt_entry_handler(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
625e1ba6d4SJohn Baldwin 
635e1ba6d4SJohn Baldwin static void	prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
645e1ba6d4SJohn Baldwin static void	prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
655e1ba6d4SJohn Baldwin static void	prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler,
665e1ba6d4SJohn Baldwin 		    void *arg);
675e1ba6d4SJohn Baldwin 
685e1ba6d4SJohn Baldwin static void
695e1ba6d4SJohn Baldwin prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler, void *arg)
705e1ba6d4SJohn Baldwin {
715e1ba6d4SJohn Baldwin     ACPI_PCI_ROUTING_TABLE *entry;
725e1ba6d4SJohn Baldwin     char *prtptr;
735e1ba6d4SJohn Baldwin 
745e1ba6d4SJohn Baldwin     /* First check to see if there is a table to walk. */
755e1ba6d4SJohn Baldwin     if (prt == NULL || prt->Pointer == NULL)
765e1ba6d4SJohn Baldwin 	return;
775e1ba6d4SJohn Baldwin 
785e1ba6d4SJohn Baldwin     /* Walk the table executing the handler function for each entry. */
795e1ba6d4SJohn Baldwin     prtptr = prt->Pointer;
805e1ba6d4SJohn Baldwin     entry = (ACPI_PCI_ROUTING_TABLE *)prtptr;
815e1ba6d4SJohn Baldwin     while (entry->Length != 0) {
825e1ba6d4SJohn Baldwin 	handler(entry, arg);
835e1ba6d4SJohn Baldwin 	prtptr += entry->Length;
845e1ba6d4SJohn Baldwin 	entry = (ACPI_PCI_ROUTING_TABLE *)prtptr;
855e1ba6d4SJohn Baldwin     }
865e1ba6d4SJohn Baldwin }
875e1ba6d4SJohn Baldwin 
885e1ba6d4SJohn Baldwin static void
895e1ba6d4SJohn Baldwin prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg)
905e1ba6d4SJohn Baldwin {
915e1ba6d4SJohn Baldwin     ACPI_HANDLE handle;
925e1ba6d4SJohn Baldwin     device_t child, pcib;
935e1ba6d4SJohn Baldwin     int error;
945e1ba6d4SJohn Baldwin 
955e1ba6d4SJohn Baldwin     /* We only care about entries that reference a link device. */
965e1ba6d4SJohn Baldwin     if (entry->Source == NULL || entry->Source[0] == '\0')
975e1ba6d4SJohn Baldwin 	return;
985e1ba6d4SJohn Baldwin 
995e1ba6d4SJohn Baldwin     /* Lookup the associated handle and device. */
1005e1ba6d4SJohn Baldwin     pcib = (device_t)arg;
1015e1ba6d4SJohn Baldwin     if (ACPI_FAILURE(AcpiGetHandle(acpi_get_handle(pcib), entry->Source,
1025e1ba6d4SJohn Baldwin 	&handle)))
1035e1ba6d4SJohn Baldwin 	return;
1045e1ba6d4SJohn Baldwin     child = acpi_get_device(handle);
1055e1ba6d4SJohn Baldwin     if (child == NULL)
1065e1ba6d4SJohn Baldwin 	return;
1075e1ba6d4SJohn Baldwin 
1085e1ba6d4SJohn Baldwin     /* If the device hasn't been probed yet, force it to do so. */
1095e1ba6d4SJohn Baldwin     error = device_probe_and_attach(child);
1105e1ba6d4SJohn Baldwin     if (error != 0) {
1115e1ba6d4SJohn Baldwin 	device_printf((device_t)arg, "failed to force attach of %s\n",
1125e1ba6d4SJohn Baldwin 	    acpi_name(handle));
1135e1ba6d4SJohn Baldwin 	return;
1145e1ba6d4SJohn Baldwin     }
1155e1ba6d4SJohn Baldwin 
1165e1ba6d4SJohn Baldwin     /* Add a reference for a specific bus/device/pin tuple. */
1175e1ba6d4SJohn Baldwin     acpi_pci_link_add_reference(child, entry->SourceIndex, pcib,
1185e1ba6d4SJohn Baldwin 	ACPI_ADR_PCI_SLOT(entry->Address), entry->Pin);
1195e1ba6d4SJohn Baldwin }
1205e1ba6d4SJohn Baldwin 
1212ccfc932SJohn Baldwin int
1222ccfc932SJohn Baldwin acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
12315e32d5dSMike Smith {
12415e32d5dSMike Smith     device_t			child;
12515e32d5dSMike Smith     ACPI_STATUS			status;
1260ae55423SMike Smith 
127b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
12815e32d5dSMike Smith 
12915e32d5dSMike Smith     /*
13015e32d5dSMike Smith      * Don't attach if we're not really there.
13115e32d5dSMike Smith      *
1322ccfc932SJohn Baldwin      * XXX: This isn't entirely correct since we may be a PCI bus
13315e32d5dSMike Smith      * on a hot-plug docking station, etc.
13415e32d5dSMike Smith      */
13515e32d5dSMike Smith     if (!acpi_DeviceIsPresent(dev))
1360ae55423SMike Smith 	return_VALUE(ENXIO);
13715e32d5dSMike Smith 
13815e32d5dSMike Smith     /*
1393304735dSNate Lawson      * Get the PCI interrupt routing table for this bus.  If we can't
14020447d54SJohn Baldwin      * get it, this is not an error but may reduce functionality.  There
14120447d54SJohn Baldwin      * are several valid bridges in the field that do not have a _PRT, so
14220447d54SJohn Baldwin      * only warn about missing tables if bootverbose is set.
1434fa387b6SMike Smith      */
1442ccfc932SJohn Baldwin     prt->Length = ACPI_ALLOCATE_BUFFER;
1452ccfc932SJohn Baldwin     status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt);
14620447d54SJohn Baldwin     if (ACPI_FAILURE(status) && (bootverbose || status != AE_NOT_FOUND))
147f144391bSPeter Wemm 	device_printf(dev,
148f144391bSPeter Wemm 	    "could not get PCI interrupt routing table for %s - %s\n",
149f144391bSPeter Wemm 	    acpi_name(acpi_get_handle(dev)), AcpiFormatException(status));
1504fa387b6SMike Smith 
1514fa387b6SMike Smith     /*
152042283a6SMike Smith      * Attach the PCI bus proper.
15315e32d5dSMike Smith      */
1542ccfc932SJohn Baldwin     if ((child = device_add_child(dev, "pci", busno)) == NULL) {
155ebc4ae3bSJohn Baldwin 	device_printf(device_get_parent(dev), "couldn't attach pci bus\n");
1560ae55423SMike Smith 	return_VALUE(ENXIO);
15715e32d5dSMike Smith     }
15815e32d5dSMike Smith 
15915e32d5dSMike Smith     /*
1603cd59d7bSScott Long      * Now go scan the bus.
16115e32d5dSMike Smith      */
1625e1ba6d4SJohn Baldwin     prt_walk_table(prt, prt_attach_devices, dev);
1633304735dSNate Lawson 
1642ccfc932SJohn Baldwin     return_VALUE (bus_generic_attach(dev));
16515e32d5dSMike Smith }
16615e32d5dSMike Smith 
167ba835e3fSMitsuru IWASAKI int
168e4116e93SNate Lawson acpi_pcib_resume(device_t dev)
169ba835e3fSMitsuru IWASAKI {
1705e1ba6d4SJohn Baldwin 
171ba835e3fSMitsuru IWASAKI     return (bus_generic_resume(dev));
172ba835e3fSMitsuru IWASAKI }
173ba835e3fSMitsuru IWASAKI 
1745e1ba6d4SJohn Baldwin static void
1755e1ba6d4SJohn Baldwin prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg)
1765e1ba6d4SJohn Baldwin {
1775e1ba6d4SJohn Baldwin     struct prt_lookup_request *pr;
1785e1ba6d4SJohn Baldwin 
1795e1ba6d4SJohn Baldwin     pr = (struct prt_lookup_request *)arg;
1805e1ba6d4SJohn Baldwin     if (pr->pr_entry != NULL)
1815e1ba6d4SJohn Baldwin 	return;
1825e1ba6d4SJohn Baldwin 
1835e1ba6d4SJohn Baldwin     /*
1845e1ba6d4SJohn Baldwin      * Compare the slot number (high word of Address) and pin number
1855e1ba6d4SJohn Baldwin      * (note that ACPI uses 0 for INTA) to check for a match.
1865e1ba6d4SJohn Baldwin      *
1875e1ba6d4SJohn Baldwin      * Note that the low word of the Address field (function number)
1885e1ba6d4SJohn Baldwin      * is required by the specification to be 0xffff.  We don't risk
1895e1ba6d4SJohn Baldwin      * checking it here.
1905e1ba6d4SJohn Baldwin      */
1915e1ba6d4SJohn Baldwin     if (ACPI_ADR_PCI_SLOT(entry->Address) == pr->pr_slot &&
1925e1ba6d4SJohn Baldwin 	entry->Pin == pr->pr_pin)
1935e1ba6d4SJohn Baldwin 	    pr->pr_entry = entry;
1945e1ba6d4SJohn Baldwin }
1955e1ba6d4SJohn Baldwin 
1964fa387b6SMike Smith /*
1974fa387b6SMike Smith  * Route an interrupt for a child of the bridge.
1984fa387b6SMike Smith  */
1992ccfc932SJohn Baldwin int
2005e1ba6d4SJohn Baldwin acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
2015e1ba6d4SJohn Baldwin     ACPI_BUFFER *prtbuf)
20215e32d5dSMike Smith {
203c1b1f787SMike Smith     ACPI_PCI_ROUTING_TABLE *prt;
2045e1ba6d4SJohn Baldwin     struct prt_lookup_request pr;
2055e1ba6d4SJohn Baldwin     ACPI_HANDLE lnkdev;
2065e1ba6d4SJohn Baldwin     int interrupt;
2074fa387b6SMike Smith 
208b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
2094c1cdee6SMike Smith 
2104dc4ea26SJohn Baldwin     interrupt = PCI_INVALID_IRQ;
2114fa387b6SMike Smith 
2123304735dSNate Lawson     /* ACPI numbers pins 0-3, not 1-4 like the BIOS. */
2134fa387b6SMike Smith     pin--;
2144fa387b6SMike Smith 
215bbf7c27aSNate Lawson     ACPI_SERIAL_BEGIN(pcib);
216bbf7c27aSNate Lawson 
2175e1ba6d4SJohn Baldwin     /* Search for a matching entry in the routing table. */
2185e1ba6d4SJohn Baldwin     pr.pr_entry = NULL;
2195e1ba6d4SJohn Baldwin     pr.pr_pin = pin;
2205e1ba6d4SJohn Baldwin     pr.pr_slot = pci_get_slot(dev);
2215e1ba6d4SJohn Baldwin     prt_walk_table(prtbuf, prt_lookup_device, &pr);
2225e1ba6d4SJohn Baldwin     if (pr.pr_entry == NULL)
2235e1ba6d4SJohn Baldwin 	return (PCI_INVALID_IRQ);
2245e1ba6d4SJohn Baldwin     prt = pr.pr_entry;
2255e1ba6d4SJohn Baldwin 
22612f57103SNate Lawson     if (bootverbose) {
22712f57103SNate Lawson 	device_printf(pcib, "matched entry for %d.%d.INT%c",
22812f57103SNate Lawson 	    pci_get_bus(dev), pci_get_slot(dev), 'A' + pin);
2295e1ba6d4SJohn Baldwin 	if (prt->Source != NULL && prt->Source[0] != '\0')
2305e1ba6d4SJohn Baldwin 		printf(" (src %s:%u)", prt->Source, prt->SourceIndex);
23112f57103SNate Lawson 	printf("\n");
23212f57103SNate Lawson     }
2334fa387b6SMike Smith 
2344fa387b6SMike Smith     /*
235e4116e93SNate Lawson      * If source is empty/NULL, the source index is a global IRQ number
236e4116e93SNate Lawson      * and it's hard-wired so we're done.
2374fa387b6SMike Smith      */
2383304735dSNate Lawson     if (prt->Source == NULL || prt->Source[0] == '\0') {
239a067d210SDoug Rabson 	if (bootverbose)
240e4116e93SNate Lawson 	    device_printf(pcib, "slot %d INT%c hardwired to IRQ %d\n",
241e4116e93SNate Lawson 		pci_get_slot(dev), 'A' + pin, prt->SourceIndex);
242e4116e93SNate Lawson 	if (prt->SourceIndex)
2434fa387b6SMike Smith 	    interrupt = prt->SourceIndex;
244e4116e93SNate Lawson 	else
245e4116e93SNate Lawson 	    device_printf(pcib, "error: invalid hard-wired IRQ of 0\n");
2464fa387b6SMike Smith 	goto out;
2474fa387b6SMike Smith     }
2484fa387b6SMike Smith 
2495e1ba6d4SJohn Baldwin     /*
2505e1ba6d4SJohn Baldwin      * We have to find the source device (PCI interrupt link device).
2515e1ba6d4SJohn Baldwin      */
2525e1ba6d4SJohn Baldwin     if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
2535e1ba6d4SJohn Baldwin 	device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
2545e1ba6d4SJohn Baldwin 	    prt->Source);
2555e1ba6d4SJohn Baldwin 	goto out;
2565e1ba6d4SJohn Baldwin     }
2575e1ba6d4SJohn Baldwin     interrupt = acpi_pci_link_route_interrupt(acpi_get_device(lnkdev),
258e4116e93SNate Lawson 	prt->SourceIndex);
2593304735dSNate Lawson 
260e4116e93SNate Lawson     if (bootverbose && PCI_INTERRUPT_VALID(interrupt))
261c6a121abSJohn Baldwin 	device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
2625e1ba6d4SJohn Baldwin 	    pci_get_slot(dev), 'A' + pin, interrupt, acpi_name(lnkdev));
2634fa387b6SMike Smith 
2644fa387b6SMike Smith out:
265bbf7c27aSNate Lawson     ACPI_SERIAL_END(pcib);
2664fa387b6SMike Smith 
2674c1cdee6SMike Smith     return_VALUE (interrupt);
2684fa387b6SMike Smith }
269