acpi_pcib.c revision 153016
133965Sjdp/*-
233965Sjdp * Copyright (c) 2000 Michael Smith
333965Sjdp * Copyright (c) 2000 BSDi
433965Sjdp * All rights reserved.
533965Sjdp *
633965Sjdp * Redistribution and use in source and binary forms, with or without
733965Sjdp * modification, are permitted provided that the following conditions
833965Sjdp * are met:
933965Sjdp * 1. Redistributions of source code must retain the above copyright
1033965Sjdp *    notice, this list of conditions and the following disclaimer.
1133965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1233965Sjdp *    notice, this list of conditions and the following disclaimer in the
1333965Sjdp *    documentation and/or other materials provided with the distribution.
1433965Sjdp *
1533965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1633965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1733965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1833965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1933965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2033965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2133965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2233965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2333965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2433965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2533965Sjdp * SUCH DAMAGE.
2633965Sjdp */
2733965Sjdp
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_pcib.c 153016 2005-12-02 13:35:14Z jhb $");
30
31#include "opt_acpi.h"
32#include <sys/param.h>
33#include <sys/bus.h>
34#include <sys/malloc.h>
35#include <sys/kernel.h>
36
37#include <contrib/dev/acpica/acpi.h>
38#include <dev/acpica/acpivar.h>
39#include <dev/acpica/acpi_pcibvar.h>
40
41#include <dev/pci/pcivar.h>
42#include "pcib_if.h"
43
44/* Hooks for the ACPI CA debugging infrastructure. */
45#define _COMPONENT	ACPI_BUS
46ACPI_MODULE_NAME("PCI")
47
48ACPI_SERIAL_DECL(pcib, "ACPI PCI bus methods");
49
50/*
51 * For locking, we assume the caller is not concurrent since this is
52 * triggered by newbus methods.
53 */
54
55struct prt_lookup_request {
56    ACPI_PCI_ROUTING_TABLE *pr_entry;
57    u_int	pr_pin;
58    u_int	pr_slot;
59};
60
61typedef	void prt_entry_handler(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
62
63static void	prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
64static void	prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg);
65static void	prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler,
66		    void *arg);
67
68static void
69prt_walk_table(ACPI_BUFFER *prt, prt_entry_handler *handler, void *arg)
70{
71    ACPI_PCI_ROUTING_TABLE *entry;
72    char *prtptr;
73
74    /* First check to see if there is a table to walk. */
75    if (prt == NULL || prt->Pointer == NULL)
76	return;
77
78    /* Walk the table executing the handler function for each entry. */
79    prtptr = prt->Pointer;
80    entry = (ACPI_PCI_ROUTING_TABLE *)prtptr;
81    while (entry->Length != 0) {
82	handler(entry, arg);
83	prtptr += entry->Length;
84	entry = (ACPI_PCI_ROUTING_TABLE *)prtptr;
85    }
86}
87
88static void
89prt_attach_devices(ACPI_PCI_ROUTING_TABLE *entry, void *arg)
90{
91    ACPI_HANDLE handle;
92    device_t child, pcib;
93    int error;
94
95    /* We only care about entries that reference a link device. */
96    if (entry->Source == NULL || entry->Source[0] == '\0')
97	return;
98
99    /*
100     * In practice, we only see SourceIndex's of 0 out in the wild.
101     * When indices != 0 have been found, they've been bugs in the ASL.
102     */
103    if (entry->SourceIndex != 0)
104	return;
105
106    /* Lookup the associated handle and device. */
107    pcib = (device_t)arg;
108    if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, entry->Source, &handle)))
109	return;
110    child = acpi_get_device(handle);
111    if (child == NULL)
112	return;
113
114    /* If the device hasn't been probed yet, force it to do so. */
115    error = device_probe_and_attach(child);
116    if (error != 0) {
117	device_printf(pcib, "failed to force attach of %s\n",
118	    acpi_name(handle));
119	return;
120    }
121
122    /* Add a reference for a specific bus/device/pin tuple. */
123    acpi_pci_link_add_reference(child, entry->SourceIndex, pcib,
124	ACPI_ADR_PCI_SLOT(entry->Address), entry->Pin);
125}
126
127int
128acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
129{
130    device_t			child;
131    ACPI_STATUS			status;
132
133    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
134
135    /*
136     * Don't attach if we're not really there.
137     *
138     * XXX: This isn't entirely correct since we may be a PCI bus
139     * on a hot-plug docking station, etc.
140     */
141    if (!acpi_DeviceIsPresent(dev))
142	return_VALUE(ENXIO);
143
144    /*
145     * Get the PCI interrupt routing table for this bus.  If we can't
146     * get it, this is not an error but may reduce functionality.  There
147     * are several valid bridges in the field that do not have a _PRT, so
148     * only warn about missing tables if bootverbose is set.
149     */
150    prt->Length = ACPI_ALLOCATE_BUFFER;
151    status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt);
152    if (ACPI_FAILURE(status) && (bootverbose || status != AE_NOT_FOUND))
153	device_printf(dev,
154	    "could not get PCI interrupt routing table for %s - %s\n",
155	    acpi_name(acpi_get_handle(dev)), AcpiFormatException(status));
156
157    /*
158     * Attach the PCI bus proper.
159     */
160    if ((child = device_add_child(dev, "pci", busno)) == NULL) {
161	device_printf(device_get_parent(dev), "couldn't attach pci bus\n");
162	return_VALUE(ENXIO);
163    }
164
165    /*
166     * Now go scan the bus.
167     */
168    prt_walk_table(prt, prt_attach_devices, dev);
169
170    return_VALUE (bus_generic_attach(dev));
171}
172
173int
174acpi_pcib_resume(device_t dev)
175{
176
177    return (bus_generic_resume(dev));
178}
179
180static void
181prt_lookup_device(ACPI_PCI_ROUTING_TABLE *entry, void *arg)
182{
183    struct prt_lookup_request *pr;
184
185    pr = (struct prt_lookup_request *)arg;
186    if (pr->pr_entry != NULL)
187	return;
188
189    /*
190     * Compare the slot number (high word of Address) and pin number
191     * (note that ACPI uses 0 for INTA) to check for a match.
192     *
193     * Note that the low word of the Address field (function number)
194     * is required by the specification to be 0xffff.  We don't risk
195     * checking it here.
196     */
197    if (ACPI_ADR_PCI_SLOT(entry->Address) == pr->pr_slot &&
198	entry->Pin == pr->pr_pin)
199	    pr->pr_entry = entry;
200}
201
202/*
203 * Route an interrupt for a child of the bridge.
204 */
205int
206acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
207    ACPI_BUFFER *prtbuf)
208{
209    ACPI_PCI_ROUTING_TABLE *prt;
210    struct prt_lookup_request pr;
211    ACPI_HANDLE lnkdev;
212    int interrupt;
213
214    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
215
216    interrupt = PCI_INVALID_IRQ;
217
218    /* ACPI numbers pins 0-3, not 1-4 like the BIOS. */
219    pin--;
220
221    ACPI_SERIAL_BEGIN(pcib);
222
223    /* Search for a matching entry in the routing table. */
224    pr.pr_entry = NULL;
225    pr.pr_pin = pin;
226    pr.pr_slot = pci_get_slot(dev);
227    prt_walk_table(prtbuf, prt_lookup_device, &pr);
228    if (pr.pr_entry == NULL) {
229	device_printf(pcib, "no PRT entry for %d.%d.INT%c\n", pci_get_bus(dev),
230	    pci_get_slot(dev), 'A' + pin);
231	goto out;
232    }
233    prt = pr.pr_entry;
234
235    if (bootverbose) {
236	device_printf(pcib, "matched entry for %d.%d.INT%c",
237	    pci_get_bus(dev), pci_get_slot(dev), 'A' + pin);
238	if (prt->Source != NULL && prt->Source[0] != '\0')
239		printf(" (src %s:%u)", prt->Source, prt->SourceIndex);
240	printf("\n");
241    }
242
243    /*
244     * If source is empty/NULL, the source index is a global IRQ number
245     * and it's hard-wired so we're done.
246     *
247     * XXX: If the source index is non-zero, ignore the source device and
248     * assume that this is a hard-wired entry.
249     */
250    if (prt->Source == NULL || prt->Source[0] == '\0' ||
251	prt->SourceIndex != 0) {
252	if (bootverbose)
253	    device_printf(pcib, "slot %d INT%c hardwired to IRQ %d\n",
254		pci_get_slot(dev), 'A' + pin, prt->SourceIndex);
255	if (prt->SourceIndex)
256	    interrupt = prt->SourceIndex;
257	else
258	    device_printf(pcib, "error: invalid hard-wired IRQ of 0\n");
259	goto out;
260    }
261
262    /*
263     * We have to find the source device (PCI interrupt link device).
264     */
265    if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
266	device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
267	    prt->Source);
268	goto out;
269    }
270    interrupt = acpi_pci_link_route_interrupt(acpi_get_device(lnkdev),
271	prt->SourceIndex);
272
273    if (bootverbose && PCI_INTERRUPT_VALID(interrupt))
274	device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
275	    pci_get_slot(dev), 'A' + pin, interrupt, acpi_name(lnkdev));
276
277out:
278    ACPI_SERIAL_END(pcib);
279
280    return_VALUE (interrupt);
281}
282