Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /*******************************************************************************
0003  *
0004  * Module Name: hwpci - Obtain PCI bus, device, and function numbers
0005  *
0006  ******************************************************************************/
0007 
0008 #include <acpi/acpi.h>
0009 #include "accommon.h"
0010 
0011 #define _COMPONENT          ACPI_NAMESPACE
0012 ACPI_MODULE_NAME("hwpci")
0013 
0014 /* PCI configuration space values */
0015 #define PCI_CFG_HEADER_TYPE_REG             0x0E
0016 #define PCI_CFG_PRIMARY_BUS_NUMBER_REG      0x18
0017 #define PCI_CFG_SECONDARY_BUS_NUMBER_REG    0x19
0018 /* PCI header values */
0019 #define PCI_HEADER_TYPE_MASK                0x7F
0020 #define PCI_TYPE_BRIDGE                     0x01
0021 #define PCI_TYPE_CARDBUS_BRIDGE             0x02
0022 typedef struct acpi_pci_device {
0023     acpi_handle device;
0024     struct acpi_pci_device *next;
0025 
0026 } acpi_pci_device;
0027 
0028 /* Local prototypes */
0029 
0030 static acpi_status
0031 acpi_hw_build_pci_list(acpi_handle root_pci_device,
0032                acpi_handle pci_region,
0033                struct acpi_pci_device **return_list_head);
0034 
0035 static acpi_status
0036 acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
0037              struct acpi_pci_device *list_head);
0038 
0039 static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head);
0040 
0041 static acpi_status
0042 acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
0043                 acpi_handle pci_device,
0044                 u16 *bus_number, u8 *is_bridge);
0045 
0046 /*******************************************************************************
0047  *
0048  * FUNCTION:    acpi_hw_derive_pci_id
0049  *
0050  * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be
0051  *                                    modified by this function.
0052  *              root_pci_device     - A handle to a PCI device object. This
0053  *                                    object must be a PCI Root Bridge having a
0054  *                                    _HID value of either PNP0A03 or PNP0A08
0055  *              pci_region          - A handle to a PCI configuration space
0056  *                                    Operation Region being initialized
0057  *
0058  * RETURN:      Status
0059  *
0060  * DESCRIPTION: This function derives a full PCI ID for a PCI device,
0061  *              consisting of a Segment number, Bus number, Device number,
0062  *              and function code.
0063  *
0064  *              The PCI hardware dynamically configures PCI bus numbers
0065  *              depending on the bus topology discovered during system
0066  *              initialization. This function is invoked during configuration
0067  *              of a PCI_Config Operation Region in order to (possibly) update
0068  *              the Bus/Device/Function numbers in the pci_id with the actual
0069  *              values as determined by the hardware and operating system
0070  *              configuration.
0071  *
0072  *              The pci_id parameter is initially populated during the Operation
0073  *              Region initialization. This function is then called, and is
0074  *              will make any necessary modifications to the Bus, Device, or
0075  *              Function number PCI ID subfields as appropriate for the
0076  *              current hardware and OS configuration.
0077  *
0078  * NOTE:        Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id
0079  *              interface since this feature is OS-independent. This module
0080  *              specifically avoids any use of recursion by building a local
0081  *              temporary device list.
0082  *
0083  ******************************************************************************/
0084 
0085 acpi_status
0086 acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
0087               acpi_handle root_pci_device, acpi_handle pci_region)
0088 {
0089     acpi_status status;
0090     struct acpi_pci_device *list_head;
0091 
0092     ACPI_FUNCTION_TRACE(hw_derive_pci_id);
0093 
0094     if (!pci_id) {
0095         return_ACPI_STATUS(AE_BAD_PARAMETER);
0096     }
0097 
0098     /* Build a list of PCI devices, from pci_region up to root_pci_device */
0099 
0100     status =
0101         acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);
0102     if (ACPI_SUCCESS(status)) {
0103 
0104         /* Walk the list, updating the PCI device/function/bus numbers */
0105 
0106         status = acpi_hw_process_pci_list(pci_id, list_head);
0107 
0108         /* Delete the list */
0109 
0110         acpi_hw_delete_pci_list(list_head);
0111     }
0112 
0113     return_ACPI_STATUS(status);
0114 }
0115 
0116 /*******************************************************************************
0117  *
0118  * FUNCTION:    acpi_hw_build_pci_list
0119  *
0120  * PARAMETERS:  root_pci_device     - A handle to a PCI device object. This
0121  *                                    object is guaranteed to be a PCI Root
0122  *                                    Bridge having a _HID value of either
0123  *                                    PNP0A03 or PNP0A08
0124  *              pci_region          - A handle to the PCI configuration space
0125  *                                    Operation Region
0126  *              return_list_head    - Where the PCI device list is returned
0127  *
0128  * RETURN:      Status
0129  *
0130  * DESCRIPTION: Builds a list of devices from the input PCI region up to the
0131  *              Root PCI device for this namespace subtree.
0132  *
0133  ******************************************************************************/
0134 
0135 static acpi_status
0136 acpi_hw_build_pci_list(acpi_handle root_pci_device,
0137                acpi_handle pci_region,
0138                struct acpi_pci_device **return_list_head)
0139 {
0140     acpi_handle current_device;
0141     acpi_handle parent_device;
0142     acpi_status status;
0143     struct acpi_pci_device *list_element;
0144 
0145     /*
0146      * Ascend namespace branch until the root_pci_device is reached, building
0147      * a list of device nodes. Loop will exit when either the PCI device is
0148      * found, or the root of the namespace is reached.
0149      */
0150     *return_list_head = NULL;
0151     current_device = pci_region;
0152     while (1) {
0153         status = acpi_get_parent(current_device, &parent_device);
0154         if (ACPI_FAILURE(status)) {
0155 
0156             /* Must delete the list before exit */
0157 
0158             acpi_hw_delete_pci_list(*return_list_head);
0159             return (status);
0160         }
0161 
0162         /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
0163 
0164         if (parent_device == root_pci_device) {
0165             return (AE_OK);
0166         }
0167 
0168         list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
0169         if (!list_element) {
0170 
0171             /* Must delete the list before exit */
0172 
0173             acpi_hw_delete_pci_list(*return_list_head);
0174             return (AE_NO_MEMORY);
0175         }
0176 
0177         /* Put new element at the head of the list */
0178 
0179         list_element->next = *return_list_head;
0180         list_element->device = parent_device;
0181         *return_list_head = list_element;
0182 
0183         current_device = parent_device;
0184     }
0185 }
0186 
0187 /*******************************************************************************
0188  *
0189  * FUNCTION:    acpi_hw_process_pci_list
0190  *
0191  * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be
0192  *                                    modified by this function.
0193  *              list_head           - Device list created by
0194  *                                    acpi_hw_build_pci_list
0195  *
0196  * RETURN:      Status
0197  *
0198  * DESCRIPTION: Walk downward through the PCI device list, getting the device
0199  *              info for each, via the PCI configuration space and updating
0200  *              the PCI ID as necessary. Deletes the list during traversal.
0201  *
0202  ******************************************************************************/
0203 
0204 static acpi_status
0205 acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
0206              struct acpi_pci_device *list_head)
0207 {
0208     acpi_status status = AE_OK;
0209     struct acpi_pci_device *info;
0210     u16 bus_number;
0211     u8 is_bridge = TRUE;
0212 
0213     ACPI_FUNCTION_NAME(hw_process_pci_list);
0214 
0215     ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
0216               "Input PciId:  Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
0217               pci_id->segment, pci_id->bus, pci_id->device,
0218               pci_id->function));
0219 
0220     bus_number = pci_id->bus;
0221 
0222     /*
0223      * Descend down the namespace tree, collecting PCI device, function,
0224      * and bus numbers. bus_number is only important for PCI bridges.
0225      * Algorithm: As we descend the tree, use the last valid PCI device,
0226      * function, and bus numbers that are discovered, and assign them
0227      * to the PCI ID for the target device.
0228      */
0229     info = list_head;
0230     while (info) {
0231         status = acpi_hw_get_pci_device_info(pci_id, info->device,
0232                              &bus_number, &is_bridge);
0233         if (ACPI_FAILURE(status)) {
0234             return (status);
0235         }
0236 
0237         info = info->next;
0238     }
0239 
0240     ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
0241               "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
0242               "Status %X BusNumber %X IsBridge %X\n",
0243               pci_id->segment, pci_id->bus, pci_id->device,
0244               pci_id->function, status, bus_number, is_bridge));
0245 
0246     return (AE_OK);
0247 }
0248 
0249 /*******************************************************************************
0250  *
0251  * FUNCTION:    acpi_hw_delete_pci_list
0252  *
0253  * PARAMETERS:  list_head           - Device list created by
0254  *                                    acpi_hw_build_pci_list
0255  *
0256  * RETURN:      None
0257  *
0258  * DESCRIPTION: Free the entire PCI list.
0259  *
0260  ******************************************************************************/
0261 
0262 static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)
0263 {
0264     struct acpi_pci_device *next;
0265     struct acpi_pci_device *previous;
0266 
0267     next = list_head;
0268     while (next) {
0269         previous = next;
0270         next = previous->next;
0271         ACPI_FREE(previous);
0272     }
0273 }
0274 
0275 /*******************************************************************************
0276  *
0277  * FUNCTION:    acpi_hw_get_pci_device_info
0278  *
0279  * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be
0280  *                                    modified by this function.
0281  *              pci_device          - Handle for the PCI device object
0282  *              bus_number          - Where a PCI bridge bus number is returned
0283  *              is_bridge           - Return value, indicates if this PCI
0284  *                                    device is a PCI bridge
0285  *
0286  * RETURN:      Status
0287  *
0288  * DESCRIPTION: Get the device info for a single PCI device object. Get the
0289  *              _ADR (contains PCI device and function numbers), and for PCI
0290  *              bridge devices, get the bus number from PCI configuration
0291  *              space.
0292  *
0293  ******************************************************************************/
0294 
0295 static acpi_status
0296 acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
0297                 acpi_handle pci_device,
0298                 u16 *bus_number, u8 *is_bridge)
0299 {
0300     acpi_status status;
0301     acpi_object_type object_type;
0302     u64 return_value;
0303     u64 pci_value;
0304 
0305     /* We only care about objects of type Device */
0306 
0307     status = acpi_get_type(pci_device, &object_type);
0308     if (ACPI_FAILURE(status)) {
0309         return (status);
0310     }
0311 
0312     if (object_type != ACPI_TYPE_DEVICE) {
0313         return (AE_OK);
0314     }
0315 
0316     /* We need an _ADR. Ignore device if not present */
0317 
0318     status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
0319                          pci_device, &return_value);
0320     if (ACPI_FAILURE(status)) {
0321         return (AE_OK);
0322     }
0323 
0324     /*
0325      * From _ADR, get the PCI Device and Function and
0326      * update the PCI ID.
0327      */
0328     pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));
0329     pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value));
0330 
0331     /*
0332      * If the previous device was a bridge, use the previous
0333      * device bus number
0334      */
0335     if (*is_bridge) {
0336         pci_id->bus = *bus_number;
0337     }
0338 
0339     /*
0340      * Get the bus numbers from PCI Config space:
0341      *
0342      * First, get the PCI header_type
0343      */
0344     *is_bridge = FALSE;
0345     status = acpi_os_read_pci_configuration(pci_id,
0346                         PCI_CFG_HEADER_TYPE_REG,
0347                         &pci_value, 8);
0348     if (ACPI_FAILURE(status)) {
0349         return (status);
0350     }
0351 
0352     /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */
0353 
0354     pci_value &= PCI_HEADER_TYPE_MASK;
0355 
0356     if ((pci_value != PCI_TYPE_BRIDGE) &&
0357         (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) {
0358         return (AE_OK);
0359     }
0360 
0361     /* Bridge: Get the Primary bus_number */
0362 
0363     status = acpi_os_read_pci_configuration(pci_id,
0364                         PCI_CFG_PRIMARY_BUS_NUMBER_REG,
0365                         &pci_value, 8);
0366     if (ACPI_FAILURE(status)) {
0367         return (status);
0368     }
0369 
0370     *is_bridge = TRUE;
0371     pci_id->bus = (u16)pci_value;
0372 
0373     /* Bridge: Get the Secondary bus_number */
0374 
0375     status = acpi_os_read_pci_configuration(pci_id,
0376                         PCI_CFG_SECONDARY_BUS_NUMBER_REG,
0377                         &pci_value, 8);
0378     if (ACPI_FAILURE(status)) {
0379         return (status);
0380     }
0381 
0382     *bus_number = (u16)pci_value;
0383     return (AE_OK);
0384 }