hwpci.c revision 306536
1/*******************************************************************************
2 *
3 * Module Name: hwpci - Obtain PCI bus, device, and function numbers
4 *
5 ******************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2016, 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;
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
202
203    /*
204     * Ascend namespace branch until the RootPciDevice is reached, building
205     * a list of device nodes. Loop will exit when either the PCI device is
206     * found, or the root of the namespace is reached.
207     */
208    *ReturnListHead = NULL;
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            return (AE_OK);
226        }
227
228        ListElement = ACPI_ALLOCATE (sizeof (ACPI_PCI_DEVICE));
229        if (!ListElement)
230        {
231            /* Must delete the list before exit */
232
233            AcpiHwDeletePciList (*ReturnListHead);
234            return (AE_NO_MEMORY);
235        }
236
237        /* Put new element at the head of the list */
238
239        ListElement->Next = *ReturnListHead;
240        ListElement->Device = ParentDevice;
241        *ReturnListHead = ListElement;
242
243        CurrentDevice = ParentDevice;
244    }
245}
246
247
248/*******************************************************************************
249 *
250 * FUNCTION:    AcpiHwProcessPciList
251 *
252 * PARAMETERS:  PciId               - Initial values for the PCI ID. May be
253 *                                    modified by this function.
254 *              ListHead            - Device list created by
255 *                                    AcpiHwBuildPciList
256 *
257 * RETURN:      Status
258 *
259 * DESCRIPTION: Walk downward through the PCI device list, getting the device
260 *              info for each, via the PCI configuration space and updating
261 *              the PCI ID as necessary. Deletes the list during traversal.
262 *
263 ******************************************************************************/
264
265static ACPI_STATUS
266AcpiHwProcessPciList (
267    ACPI_PCI_ID             *PciId,
268    ACPI_PCI_DEVICE         *ListHead)
269{
270    ACPI_STATUS             Status = AE_OK;
271    ACPI_PCI_DEVICE         *Info;
272    UINT16                  BusNumber;
273    BOOLEAN                 IsBridge = TRUE;
274
275
276    ACPI_FUNCTION_NAME (HwProcessPciList);
277
278
279    ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
280        "Input PciId:  Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
281        PciId->Segment, PciId->Bus, PciId->Device, PciId->Function));
282
283    BusNumber = PciId->Bus;
284
285    /*
286     * Descend down the namespace tree, collecting PCI device, function,
287     * and bus numbers. BusNumber is only important for PCI bridges.
288     * Algorithm: As we descend the tree, use the last valid PCI device,
289     * function, and bus numbers that are discovered, and assign them
290     * to the PCI ID for the target device.
291     */
292    Info = ListHead;
293    while (Info)
294    {
295        Status = AcpiHwGetPciDeviceInfo (PciId, Info->Device,
296            &BusNumber, &IsBridge);
297        if (ACPI_FAILURE (Status))
298        {
299            return (Status);
300        }
301
302        Info = Info->Next;
303    }
304
305    ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
306        "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
307        "Status %X BusNumber %X IsBridge %X\n",
308        PciId->Segment, PciId->Bus, PciId->Device, PciId->Function,
309        Status, BusNumber, IsBridge));
310
311    return (AE_OK);
312}
313
314
315/*******************************************************************************
316 *
317 * FUNCTION:    AcpiHwDeletePciList
318 *
319 * PARAMETERS:  ListHead            - Device list created by
320 *                                    AcpiHwBuildPciList
321 *
322 * RETURN:      None
323 *
324 * DESCRIPTION: Free the entire PCI list.
325 *
326 ******************************************************************************/
327
328static void
329AcpiHwDeletePciList (
330    ACPI_PCI_DEVICE         *ListHead)
331{
332    ACPI_PCI_DEVICE         *Next;
333    ACPI_PCI_DEVICE         *Previous;
334
335
336    Next = ListHead;
337    while (Next)
338    {
339        Previous = Next;
340        Next = Previous->Next;
341        ACPI_FREE (Previous);
342    }
343}
344
345
346/*******************************************************************************
347 *
348 * FUNCTION:    AcpiHwGetPciDeviceInfo
349 *
350 * PARAMETERS:  PciId               - Initial values for the PCI ID. May be
351 *                                    modified by this function.
352 *              PciDevice           - Handle for the PCI device object
353 *              BusNumber           - Where a PCI bridge bus number is returned
354 *              IsBridge            - Return value, indicates if this PCI
355 *                                    device is a PCI bridge
356 *
357 * RETURN:      Status
358 *
359 * DESCRIPTION: Get the device info for a single PCI device object. Get the
360 *              _ADR (contains PCI device and function numbers), and for PCI
361 *              bridge devices, get the bus number from PCI configuration
362 *              space.
363 *
364 ******************************************************************************/
365
366static ACPI_STATUS
367AcpiHwGetPciDeviceInfo (
368    ACPI_PCI_ID             *PciId,
369    ACPI_HANDLE             PciDevice,
370    UINT16                  *BusNumber,
371    BOOLEAN                 *IsBridge)
372{
373    ACPI_STATUS             Status;
374    ACPI_OBJECT_TYPE        ObjectType;
375    UINT64                  ReturnValue;
376    UINT64                  PciValue;
377
378
379    /* We only care about objects of type Device */
380
381    Status = AcpiGetType (PciDevice, &ObjectType);
382    if (ACPI_FAILURE (Status))
383    {
384        return (Status);
385    }
386
387    if (ObjectType != ACPI_TYPE_DEVICE)
388    {
389        return (AE_OK);
390    }
391
392    /* We need an _ADR. Ignore device if not present */
393
394    Status = AcpiUtEvaluateNumericObject (METHOD_NAME__ADR,
395        PciDevice, &ReturnValue);
396    if (ACPI_FAILURE (Status))
397    {
398        return (AE_OK);
399    }
400
401    /*
402     * From _ADR, get the PCI Device and Function and
403     * update the PCI ID.
404     */
405    PciId->Device = ACPI_HIWORD (ACPI_LODWORD (ReturnValue));
406    PciId->Function = ACPI_LOWORD (ACPI_LODWORD (ReturnValue));
407
408    /*
409     * If the previous device was a bridge, use the previous
410     * device bus number
411     */
412    if (*IsBridge)
413    {
414        PciId->Bus = *BusNumber;
415    }
416
417    /*
418     * Get the bus numbers from PCI Config space:
419     *
420     * First, get the PCI HeaderType
421     */
422    *IsBridge = FALSE;
423    Status = AcpiOsReadPciConfiguration (PciId,
424        PCI_CFG_HEADER_TYPE_REG, &PciValue, 8);
425    if (ACPI_FAILURE (Status))
426    {
427        return (Status);
428    }
429
430    /* We only care about bridges (1=PciBridge, 2=CardBusBridge) */
431
432    PciValue &= PCI_HEADER_TYPE_MASK;
433
434    if ((PciValue != PCI_TYPE_BRIDGE) &&
435        (PciValue != PCI_TYPE_CARDBUS_BRIDGE))
436    {
437        return (AE_OK);
438    }
439
440    /* Bridge: Get the Primary BusNumber */
441
442    Status = AcpiOsReadPciConfiguration (PciId,
443        PCI_CFG_PRIMARY_BUS_NUMBER_REG, &PciValue, 8);
444    if (ACPI_FAILURE (Status))
445    {
446        return (Status);
447    }
448
449    *IsBridge = TRUE;
450    PciId->Bus = (UINT16) PciValue;
451
452    /* Bridge: Get the Secondary BusNumber */
453
454    Status = AcpiOsReadPciConfiguration (PciId,
455        PCI_CFG_SECONDARY_BUS_NUMBER_REG, &PciValue, 8);
456    if (ACPI_FAILURE (Status))
457    {
458        return (Status);
459    }
460
461    *BusNumber = (UINT16) PciValue;
462    return (AE_OK);
463}
464