hwpci.c revision 281075
1/*******************************************************************************
2 *
3 * Module Name: hwpci - Obtain PCI bus, device, and function numbers
4 *
5 ******************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2015, Intel Corp.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions, and the following disclaimer,
16 *    without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 *    substantially similar to the "NO WARRANTY" disclaimer below
19 *    ("Disclaimer") and any redistribution must be conditioned upon
20 *    including a substantially similar Disclaimer requirement for further
21 *    binary redistribution.
22 * 3. Neither the names of the above-listed copyright holders nor the names
23 *    of any contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * Alternatively, this software may be distributed under the terms of the
27 * GNU General Public License ("GPL") version 2 as published by the Free
28 * Software Foundation.
29 *
30 * NO WARRANTY
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 * POSSIBILITY OF SUCH DAMAGES.
42 */
43
44#include <contrib/dev/acpica/include/acpi.h>
45#include <contrib/dev/acpica/include/accommon.h>
46
47
48#define _COMPONENT          ACPI_NAMESPACE
49        ACPI_MODULE_NAME    ("hwpci")
50
51
52/* PCI configuration space values */
53
54#define PCI_CFG_HEADER_TYPE_REG             0x0E
55#define PCI_CFG_PRIMARY_BUS_NUMBER_REG      0x18
56#define PCI_CFG_SECONDARY_BUS_NUMBER_REG    0x19
57
58/* PCI header values */
59
60#define PCI_HEADER_TYPE_MASK                0x7F
61#define PCI_TYPE_BRIDGE                     0x01
62#define PCI_TYPE_CARDBUS_BRIDGE             0x02
63
64typedef struct acpi_pci_device
65{
66    ACPI_HANDLE             Device;
67    struct acpi_pci_device  *Next;
68
69} ACPI_PCI_DEVICE;
70
71
72/* Local prototypes */
73
74static ACPI_STATUS
75AcpiHwBuildPciList (
76    ACPI_HANDLE             RootPciDevice,
77    ACPI_HANDLE             PciRegion,
78    ACPI_PCI_DEVICE         **ReturnListHead);
79
80static ACPI_STATUS
81AcpiHwProcessPciList (
82    ACPI_PCI_ID             *PciId,
83    ACPI_PCI_DEVICE         *ListHead);
84
85static void
86AcpiHwDeletePciList (
87    ACPI_PCI_DEVICE         *ListHead);
88
89static ACPI_STATUS
90AcpiHwGetPciDeviceInfo (
91    ACPI_PCI_ID             *PciId,
92    ACPI_HANDLE             PciDevice,
93    UINT16                  *BusNumber,
94    BOOLEAN                 *IsBridge);
95
96
97/*******************************************************************************
98 *
99 * FUNCTION:    AcpiHwDerivePciId
100 *
101 * PARAMETERS:  PciId               - Initial values for the PCI ID. May be
102 *                                    modified by this function.
103 *              RootPciDevice       - A handle to a PCI device object. This
104 *                                    object must be a PCI Root Bridge having a
105 *                                    _HID value of either PNP0A03 or PNP0A08
106 *              PciRegion           - A handle to a PCI configuration space
107 *                                    Operation Region being initialized
108 *
109 * RETURN:      Status
110 *
111 * DESCRIPTION: This function derives a full PCI ID for a PCI device,
112 *              consisting of a Segment number, Bus number, Device number,
113 *              and function code.
114 *
115 *              The PCI hardware dynamically configures PCI bus numbers
116 *              depending on the bus topology discovered during system
117 *              initialization. This function is invoked during configuration
118 *              of a PCI_Config Operation Region in order to (possibly) update
119 *              the Bus/Device/Function numbers in the PciId with the actual
120 *              values as determined by the hardware and operating system
121 *              configuration.
122 *
123 *              The PciId parameter is initially populated during the Operation
124 *              Region initialization. This function is then called, and is
125 *              will make any necessary modifications to the Bus, Device, or
126 *              Function number PCI ID subfields as appropriate for the
127 *              current hardware and OS configuration.
128 *
129 * NOTE:        Created 08/2010. Replaces the previous OSL AcpiOsDerivePciId
130 *              interface since this feature is OS-independent. This module
131 *              specifically avoids any use of recursion by building a local
132 *              temporary device list.
133 *
134 ******************************************************************************/
135
136ACPI_STATUS
137AcpiHwDerivePciId (
138    ACPI_PCI_ID             *PciId,
139    ACPI_HANDLE             RootPciDevice,
140    ACPI_HANDLE             PciRegion)
141{
142    ACPI_STATUS             Status;
143    ACPI_PCI_DEVICE         *ListHead = NULL;
144
145
146    ACPI_FUNCTION_TRACE (HwDerivePciId);
147
148
149    if (!PciId)
150    {
151        return_ACPI_STATUS (AE_BAD_PARAMETER);
152    }
153
154    /* Build a list of PCI devices, from PciRegion up to RootPciDevice */
155
156    Status = AcpiHwBuildPciList (RootPciDevice, PciRegion, &ListHead);
157    if (ACPI_SUCCESS (Status))
158    {
159        /* Walk the list, updating the PCI device/function/bus numbers */
160
161        Status = AcpiHwProcessPciList (PciId, ListHead);
162
163        /* Delete the list */
164
165        AcpiHwDeletePciList (ListHead);
166    }
167
168    return_ACPI_STATUS (Status);
169}
170
171
172/*******************************************************************************
173 *
174 * FUNCTION:    AcpiHwBuildPciList
175 *
176 * PARAMETERS:  RootPciDevice       - A handle to a PCI device object. This
177 *                                    object is guaranteed to be a PCI Root
178 *                                    Bridge having a _HID value of either
179 *                                    PNP0A03 or PNP0A08
180 *              PciRegion           - A handle to the PCI configuration space
181 *                                    Operation Region
182 *              ReturnListHead      - Where the PCI device list is returned
183 *
184 * RETURN:      Status
185 *
186 * DESCRIPTION: Builds a list of devices from the input PCI region up to the
187 *              Root PCI device for this namespace subtree.
188 *
189 ******************************************************************************/
190
191static ACPI_STATUS
192AcpiHwBuildPciList (
193    ACPI_HANDLE             RootPciDevice,
194    ACPI_HANDLE             PciRegion,
195    ACPI_PCI_DEVICE         **ReturnListHead)
196{
197    ACPI_HANDLE             CurrentDevice;
198    ACPI_HANDLE             ParentDevice;
199    ACPI_STATUS             Status;
200    ACPI_PCI_DEVICE         *ListElement;
201    ACPI_PCI_DEVICE         *ListHead = NULL;
202
203
204    /*
205     * Ascend namespace branch until the RootPciDevice is reached, building
206     * a list of device nodes. Loop will exit when either the PCI device is
207     * found, or the root of the namespace is reached.
208     */
209    CurrentDevice = PciRegion;
210    while (1)
211    {
212        Status = AcpiGetParent (CurrentDevice, &ParentDevice);
213        if (ACPI_FAILURE (Status))
214        {
215            /* Must delete the list before exit */
216
217            AcpiHwDeletePciList (*ReturnListHead);
218            return (Status);
219        }
220
221        /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
222
223        if (ParentDevice == RootPciDevice)
224        {
225            *ReturnListHead = ListHead;
226            return (AE_OK);
227        }
228
229        ListElement = ACPI_ALLOCATE (sizeof (ACPI_PCI_DEVICE));
230        if (!ListElement)
231        {
232            /* Must delete the list before exit */
233
234            AcpiHwDeletePciList (*ReturnListHead);
235            return (AE_NO_MEMORY);
236        }
237
238        /* Put new element at the head of the list */
239
240        ListElement->Next = ListHead;
241        ListElement->Device = ParentDevice;
242        ListHead = ListElement;
243
244        CurrentDevice = ParentDevice;
245    }
246}
247
248
249/*******************************************************************************
250 *
251 * FUNCTION:    AcpiHwProcessPciList
252 *
253 * PARAMETERS:  PciId               - Initial values for the PCI ID. May be
254 *                                    modified by this function.
255 *              ListHead            - Device list created by
256 *                                    AcpiHwBuildPciList
257 *
258 * RETURN:      Status
259 *
260 * DESCRIPTION: Walk downward through the PCI device list, getting the device
261 *              info for each, via the PCI configuration space and updating
262 *              the PCI ID as necessary. Deletes the list during traversal.
263 *
264 ******************************************************************************/
265
266static ACPI_STATUS
267AcpiHwProcessPciList (
268    ACPI_PCI_ID             *PciId,
269    ACPI_PCI_DEVICE         *ListHead)
270{
271    ACPI_STATUS             Status = AE_OK;
272    ACPI_PCI_DEVICE         *Info;
273    UINT16                  BusNumber;
274    BOOLEAN                 IsBridge = TRUE;
275
276
277    ACPI_FUNCTION_NAME (HwProcessPciList);
278
279
280    ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
281        "Input PciId:  Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
282        PciId->Segment, PciId->Bus, PciId->Device, PciId->Function));
283
284    BusNumber = PciId->Bus;
285
286    /*
287     * Descend down the namespace tree, collecting PCI device, function,
288     * and bus numbers. BusNumber is only important for PCI bridges.
289     * Algorithm: As we descend the tree, use the last valid PCI device,
290     * function, and bus numbers that are discovered, and assign them
291     * to the PCI ID for the target device.
292     */
293    Info = ListHead;
294    while (Info)
295    {
296        Status = AcpiHwGetPciDeviceInfo (PciId, Info->Device,
297            &BusNumber, &IsBridge);
298        if (ACPI_FAILURE (Status))
299        {
300            return (Status);
301        }
302
303        Info = Info->Next;
304    }
305
306    ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
307        "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
308        "Status %X BusNumber %X IsBridge %X\n",
309        PciId->Segment, PciId->Bus, PciId->Device, PciId->Function,
310        Status, BusNumber, IsBridge));
311
312    return (AE_OK);
313}
314
315
316/*******************************************************************************
317 *
318 * FUNCTION:    AcpiHwDeletePciList
319 *
320 * PARAMETERS:  ListHead            - Device list created by
321 *                                    AcpiHwBuildPciList
322 *
323 * RETURN:      None
324 *
325 * DESCRIPTION: Free the entire PCI list.
326 *
327 ******************************************************************************/
328
329static void
330AcpiHwDeletePciList (
331    ACPI_PCI_DEVICE         *ListHead)
332{
333    ACPI_PCI_DEVICE         *Next;
334    ACPI_PCI_DEVICE         *Previous;
335
336
337    Next = ListHead;
338    while (Next)
339    {
340        Previous = Next;
341        Next = Previous->Next;
342        ACPI_FREE (Previous);
343    }
344}
345
346
347/*******************************************************************************
348 *
349 * FUNCTION:    AcpiHwGetPciDeviceInfo
350 *
351 * PARAMETERS:  PciId               - Initial values for the PCI ID. May be
352 *                                    modified by this function.
353 *              PciDevice           - Handle for the PCI device object
354 *              BusNumber           - Where a PCI bridge bus number is returned
355 *              IsBridge            - Return value, indicates if this PCI
356 *                                    device is a PCI bridge
357 *
358 * RETURN:      Status
359 *
360 * DESCRIPTION: Get the device info for a single PCI device object. Get the
361 *              _ADR (contains PCI device and function numbers), and for PCI
362 *              bridge devices, get the bus number from PCI configuration
363 *              space.
364 *
365 ******************************************************************************/
366
367static ACPI_STATUS
368AcpiHwGetPciDeviceInfo (
369    ACPI_PCI_ID             *PciId,
370    ACPI_HANDLE             PciDevice,
371    UINT16                  *BusNumber,
372    BOOLEAN                 *IsBridge)
373{
374    ACPI_STATUS             Status;
375    ACPI_OBJECT_TYPE        ObjectType;
376    UINT64                  ReturnValue;
377    UINT64                  PciValue;
378
379
380    /* We only care about objects of type Device */
381
382    Status = AcpiGetType (PciDevice, &ObjectType);
383    if (ACPI_FAILURE (Status))
384    {
385        return (Status);
386    }
387
388    if (ObjectType != ACPI_TYPE_DEVICE)
389    {
390        return (AE_OK);
391    }
392
393    /* We need an _ADR. Ignore device if not present */
394
395    Status = AcpiUtEvaluateNumericObject (METHOD_NAME__ADR,
396        PciDevice, &ReturnValue);
397    if (ACPI_FAILURE (Status))
398    {
399        return (AE_OK);
400    }
401
402    /*
403     * From _ADR, get the PCI Device and Function and
404     * update the PCI ID.
405     */
406    PciId->Device = ACPI_HIWORD (ACPI_LODWORD (ReturnValue));
407    PciId->Function = ACPI_LOWORD (ACPI_LODWORD (ReturnValue));
408
409    /*
410     * If the previous device was a bridge, use the previous
411     * device bus number
412     */
413    if (*IsBridge)
414    {
415        PciId->Bus = *BusNumber;
416    }
417
418    /*
419     * Get the bus numbers from PCI Config space:
420     *
421     * First, get the PCI HeaderType
422     */
423    *IsBridge = FALSE;
424    Status = AcpiOsReadPciConfiguration (PciId,
425        PCI_CFG_HEADER_TYPE_REG, &PciValue, 8);
426    if (ACPI_FAILURE (Status))
427    {
428        return (Status);
429    }
430
431    /* We only care about bridges (1=PciBridge, 2=CardBusBridge) */
432
433    PciValue &= PCI_HEADER_TYPE_MASK;
434
435    if ((PciValue != PCI_TYPE_BRIDGE) &&
436        (PciValue != PCI_TYPE_CARDBUS_BRIDGE))
437    {
438        return (AE_OK);
439    }
440
441    /* Bridge: Get the Primary BusNumber */
442
443    Status = AcpiOsReadPciConfiguration (PciId,
444        PCI_CFG_PRIMARY_BUS_NUMBER_REG, &PciValue, 8);
445    if (ACPI_FAILURE (Status))
446    {
447        return (Status);
448    }
449
450    *IsBridge = TRUE;
451    PciId->Bus = (UINT16) PciValue;
452
453    /* Bridge: Get the Secondary BusNumber */
454
455    Status = AcpiOsReadPciConfiguration (PciId,
456        PCI_CFG_SECONDARY_BUS_NUMBER_REG, &PciValue, 8);
457    if (ACPI_FAILURE (Status))
458    {
459        return (Status);
460    }
461
462    *BusNumber = (UINT16) PciValue;
463    return (AE_OK);
464}
465