xref: /reactos/drivers/bus/acpi/acpica/hardware/hwpci.c (revision 03b24380)
1 /*******************************************************************************
2  *
3  * Module Name: hwpci - Obtain PCI bus, device, and function numbers
4  *
5  ******************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2022, 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 MERCHANTABILITY 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 "acpi.h"
45 #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 
64 typedef 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 
74 static ACPI_STATUS
75 AcpiHwBuildPciList (
76     ACPI_HANDLE             RootPciDevice,
77     ACPI_HANDLE             PciRegion,
78     ACPI_PCI_DEVICE         **ReturnListHead);
79 
80 static ACPI_STATUS
81 AcpiHwProcessPciList (
82     ACPI_PCI_ID             *PciId,
83     ACPI_PCI_DEVICE         *ListHead);
84 
85 static void
86 AcpiHwDeletePciList (
87     ACPI_PCI_DEVICE         *ListHead);
88 
89 static ACPI_STATUS
90 AcpiHwGetPciDeviceInfo (
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 
136 ACPI_STATUS
AcpiHwDerivePciId(ACPI_PCI_ID * PciId,ACPI_HANDLE RootPciDevice,ACPI_HANDLE PciRegion)137 AcpiHwDerivePciId (
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 
191 static ACPI_STATUS
AcpiHwBuildPciList(ACPI_HANDLE RootPciDevice,ACPI_HANDLE PciRegion,ACPI_PCI_DEVICE ** ReturnListHead)192 AcpiHwBuildPciList (
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 
265 static ACPI_STATUS
AcpiHwProcessPciList(ACPI_PCI_ID * PciId,ACPI_PCI_DEVICE * ListHead)266 AcpiHwProcessPciList (
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 
328 static void
AcpiHwDeletePciList(ACPI_PCI_DEVICE * ListHead)329 AcpiHwDeletePciList (
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 
366 static ACPI_STATUS
AcpiHwGetPciDeviceInfo(ACPI_PCI_ID * PciId,ACPI_HANDLE PciDevice,UINT16 * BusNumber,BOOLEAN * IsBridge)367 AcpiHwGetPciDeviceInfo (
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