1*95857638SErik Schmauss // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
295abccb5SBob Moore /*******************************************************************************
395abccb5SBob Moore *
495abccb5SBob Moore * Module Name: hwpci - Obtain PCI bus, device, and function numbers
595abccb5SBob Moore *
695abccb5SBob Moore ******************************************************************************/
795abccb5SBob Moore
895abccb5SBob Moore #include <acpi/acpi.h>
995abccb5SBob Moore #include "accommon.h"
1095abccb5SBob Moore
1195abccb5SBob Moore #define _COMPONENT ACPI_NAMESPACE
1295abccb5SBob Moore ACPI_MODULE_NAME("hwpci")
1395abccb5SBob Moore
1495abccb5SBob Moore /* PCI configuration space values */
1595abccb5SBob Moore #define PCI_CFG_HEADER_TYPE_REG 0x0E
1695abccb5SBob Moore #define PCI_CFG_PRIMARY_BUS_NUMBER_REG 0x18
1795abccb5SBob Moore #define PCI_CFG_SECONDARY_BUS_NUMBER_REG 0x19
1895abccb5SBob Moore /* PCI header values */
1995abccb5SBob Moore #define PCI_HEADER_TYPE_MASK 0x7F
2095abccb5SBob Moore #define PCI_TYPE_BRIDGE 0x01
2195abccb5SBob Moore #define PCI_TYPE_CARDBUS_BRIDGE 0x02
2295abccb5SBob Moore typedef struct acpi_pci_device {
2395abccb5SBob Moore acpi_handle device;
2495abccb5SBob Moore struct acpi_pci_device *next;
2595abccb5SBob Moore
2695abccb5SBob Moore } acpi_pci_device;
2795abccb5SBob Moore
2895abccb5SBob Moore /* Local prototypes */
2995abccb5SBob Moore
3095abccb5SBob Moore static acpi_status
3195abccb5SBob Moore acpi_hw_build_pci_list(acpi_handle root_pci_device,
3295abccb5SBob Moore acpi_handle pci_region,
3395abccb5SBob Moore struct acpi_pci_device **return_list_head);
3495abccb5SBob Moore
3595abccb5SBob Moore static acpi_status
3695abccb5SBob Moore acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
3795abccb5SBob Moore struct acpi_pci_device *list_head);
3895abccb5SBob Moore
3995abccb5SBob Moore static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head);
4095abccb5SBob Moore
4195abccb5SBob Moore static acpi_status
4295abccb5SBob Moore acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
4395abccb5SBob Moore acpi_handle pci_device,
4495abccb5SBob Moore u16 *bus_number, u8 *is_bridge);
4595abccb5SBob Moore
4695abccb5SBob Moore /*******************************************************************************
4795abccb5SBob Moore *
4895abccb5SBob Moore * FUNCTION: acpi_hw_derive_pci_id
4995abccb5SBob Moore *
5095abccb5SBob Moore * PARAMETERS: pci_id - Initial values for the PCI ID. May be
5195abccb5SBob Moore * modified by this function.
5295abccb5SBob Moore * root_pci_device - A handle to a PCI device object. This
5395abccb5SBob Moore * object must be a PCI Root Bridge having a
5495abccb5SBob Moore * _HID value of either PNP0A03 or PNP0A08
5595abccb5SBob Moore * pci_region - A handle to a PCI configuration space
5695abccb5SBob Moore * Operation Region being initialized
5795abccb5SBob Moore *
5895abccb5SBob Moore * RETURN: Status
5995abccb5SBob Moore *
6095abccb5SBob Moore * DESCRIPTION: This function derives a full PCI ID for a PCI device,
6195abccb5SBob Moore * consisting of a Segment number, Bus number, Device number,
6295abccb5SBob Moore * and function code.
6395abccb5SBob Moore *
6495abccb5SBob Moore * The PCI hardware dynamically configures PCI bus numbers
6595abccb5SBob Moore * depending on the bus topology discovered during system
6695abccb5SBob Moore * initialization. This function is invoked during configuration
6795abccb5SBob Moore * of a PCI_Config Operation Region in order to (possibly) update
6895abccb5SBob Moore * the Bus/Device/Function numbers in the pci_id with the actual
6995abccb5SBob Moore * values as determined by the hardware and operating system
7095abccb5SBob Moore * configuration.
7195abccb5SBob Moore *
7295abccb5SBob Moore * The pci_id parameter is initially populated during the Operation
7395abccb5SBob Moore * Region initialization. This function is then called, and is
7495abccb5SBob Moore * will make any necessary modifications to the Bus, Device, or
7595abccb5SBob Moore * Function number PCI ID subfields as appropriate for the
7695abccb5SBob Moore * current hardware and OS configuration.
7795abccb5SBob Moore *
7895abccb5SBob Moore * NOTE: Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id
7995abccb5SBob Moore * interface since this feature is OS-independent. This module
8095abccb5SBob Moore * specifically avoids any use of recursion by building a local
8195abccb5SBob Moore * temporary device list.
8295abccb5SBob Moore *
8395abccb5SBob Moore ******************************************************************************/
8495abccb5SBob Moore
8595abccb5SBob Moore acpi_status
acpi_hw_derive_pci_id(struct acpi_pci_id * pci_id,acpi_handle root_pci_device,acpi_handle pci_region)8695abccb5SBob Moore acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
8795abccb5SBob Moore acpi_handle root_pci_device, acpi_handle pci_region)
8895abccb5SBob Moore {
8995abccb5SBob Moore acpi_status status;
902900d56fSLv Zheng struct acpi_pci_device *list_head;
9195abccb5SBob Moore
9295abccb5SBob Moore ACPI_FUNCTION_TRACE(hw_derive_pci_id);
9395abccb5SBob Moore
9495abccb5SBob Moore if (!pci_id) {
9595abccb5SBob Moore return_ACPI_STATUS(AE_BAD_PARAMETER);
9695abccb5SBob Moore }
9795abccb5SBob Moore
9895abccb5SBob Moore /* Build a list of PCI devices, from pci_region up to root_pci_device */
9995abccb5SBob Moore
10095abccb5SBob Moore status =
10195abccb5SBob Moore acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);
10295abccb5SBob Moore if (ACPI_SUCCESS(status)) {
10395abccb5SBob Moore
10495abccb5SBob Moore /* Walk the list, updating the PCI device/function/bus numbers */
10595abccb5SBob Moore
10695abccb5SBob Moore status = acpi_hw_process_pci_list(pci_id, list_head);
10795abccb5SBob Moore
108c28fa24bSDavid E. Box /* Delete the list */
10995abccb5SBob Moore
11095abccb5SBob Moore acpi_hw_delete_pci_list(list_head);
111c28fa24bSDavid E. Box }
112c28fa24bSDavid E. Box
11395abccb5SBob Moore return_ACPI_STATUS(status);
11495abccb5SBob Moore }
11595abccb5SBob Moore
11695abccb5SBob Moore /*******************************************************************************
11795abccb5SBob Moore *
11895abccb5SBob Moore * FUNCTION: acpi_hw_build_pci_list
11995abccb5SBob Moore *
12095abccb5SBob Moore * PARAMETERS: root_pci_device - A handle to a PCI device object. This
12195abccb5SBob Moore * object is guaranteed to be a PCI Root
12295abccb5SBob Moore * Bridge having a _HID value of either
12395abccb5SBob Moore * PNP0A03 or PNP0A08
12495abccb5SBob Moore * pci_region - A handle to the PCI configuration space
12595abccb5SBob Moore * Operation Region
12695abccb5SBob Moore * return_list_head - Where the PCI device list is returned
12795abccb5SBob Moore *
12895abccb5SBob Moore * RETURN: Status
12995abccb5SBob Moore *
13095abccb5SBob Moore * DESCRIPTION: Builds a list of devices from the input PCI region up to the
13195abccb5SBob Moore * Root PCI device for this namespace subtree.
13295abccb5SBob Moore *
13395abccb5SBob Moore ******************************************************************************/
13495abccb5SBob Moore
13595abccb5SBob Moore static acpi_status
acpi_hw_build_pci_list(acpi_handle root_pci_device,acpi_handle pci_region,struct acpi_pci_device ** return_list_head)13695abccb5SBob Moore acpi_hw_build_pci_list(acpi_handle root_pci_device,
13795abccb5SBob Moore acpi_handle pci_region,
13895abccb5SBob Moore struct acpi_pci_device **return_list_head)
13995abccb5SBob Moore {
14095abccb5SBob Moore acpi_handle current_device;
14195abccb5SBob Moore acpi_handle parent_device;
14295abccb5SBob Moore acpi_status status;
14395abccb5SBob Moore struct acpi_pci_device *list_element;
14495abccb5SBob Moore
14595abccb5SBob Moore /*
14695abccb5SBob Moore * Ascend namespace branch until the root_pci_device is reached, building
14795abccb5SBob Moore * a list of device nodes. Loop will exit when either the PCI device is
14895abccb5SBob Moore * found, or the root of the namespace is reached.
14995abccb5SBob Moore */
1502900d56fSLv Zheng *return_list_head = NULL;
15195abccb5SBob Moore current_device = pci_region;
15295abccb5SBob Moore while (1) {
15395abccb5SBob Moore status = acpi_get_parent(current_device, &parent_device);
15495abccb5SBob Moore if (ACPI_FAILURE(status)) {
155c28fa24bSDavid E. Box
156c28fa24bSDavid E. Box /* Must delete the list before exit */
157c28fa24bSDavid E. Box
158c28fa24bSDavid E. Box acpi_hw_delete_pci_list(*return_list_head);
15995abccb5SBob Moore return (status);
16095abccb5SBob Moore }
16195abccb5SBob Moore
16295abccb5SBob Moore /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
16395abccb5SBob Moore
16495abccb5SBob Moore if (parent_device == root_pci_device) {
16595abccb5SBob Moore return (AE_OK);
16695abccb5SBob Moore }
16795abccb5SBob Moore
16895abccb5SBob Moore list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
16995abccb5SBob Moore if (!list_element) {
170c28fa24bSDavid E. Box
171c28fa24bSDavid E. Box /* Must delete the list before exit */
172c28fa24bSDavid E. Box
173c28fa24bSDavid E. Box acpi_hw_delete_pci_list(*return_list_head);
17495abccb5SBob Moore return (AE_NO_MEMORY);
17595abccb5SBob Moore }
17695abccb5SBob Moore
17795abccb5SBob Moore /* Put new element at the head of the list */
17895abccb5SBob Moore
1792900d56fSLv Zheng list_element->next = *return_list_head;
18095abccb5SBob Moore list_element->device = parent_device;
1812900d56fSLv Zheng *return_list_head = list_element;
18295abccb5SBob Moore
18395abccb5SBob Moore current_device = parent_device;
18495abccb5SBob Moore }
18595abccb5SBob Moore }
18695abccb5SBob Moore
18795abccb5SBob Moore /*******************************************************************************
18895abccb5SBob Moore *
18995abccb5SBob Moore * FUNCTION: acpi_hw_process_pci_list
19095abccb5SBob Moore *
19195abccb5SBob Moore * PARAMETERS: pci_id - Initial values for the PCI ID. May be
19295abccb5SBob Moore * modified by this function.
19395abccb5SBob Moore * list_head - Device list created by
19495abccb5SBob Moore * acpi_hw_build_pci_list
19595abccb5SBob Moore *
19695abccb5SBob Moore * RETURN: Status
19795abccb5SBob Moore *
19895abccb5SBob Moore * DESCRIPTION: Walk downward through the PCI device list, getting the device
19995abccb5SBob Moore * info for each, via the PCI configuration space and updating
20095abccb5SBob Moore * the PCI ID as necessary. Deletes the list during traversal.
20195abccb5SBob Moore *
20295abccb5SBob Moore ******************************************************************************/
20395abccb5SBob Moore
20495abccb5SBob Moore static acpi_status
acpi_hw_process_pci_list(struct acpi_pci_id * pci_id,struct acpi_pci_device * list_head)20595abccb5SBob Moore acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
20695abccb5SBob Moore struct acpi_pci_device *list_head)
20795abccb5SBob Moore {
20895abccb5SBob Moore acpi_status status = AE_OK;
20995abccb5SBob Moore struct acpi_pci_device *info;
21095abccb5SBob Moore u16 bus_number;
21195abccb5SBob Moore u8 is_bridge = TRUE;
21295abccb5SBob Moore
21395abccb5SBob Moore ACPI_FUNCTION_NAME(hw_process_pci_list);
21495abccb5SBob Moore
21595abccb5SBob Moore ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
21695abccb5SBob Moore "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
21795abccb5SBob Moore pci_id->segment, pci_id->bus, pci_id->device,
21895abccb5SBob Moore pci_id->function));
21995abccb5SBob Moore
22095abccb5SBob Moore bus_number = pci_id->bus;
22195abccb5SBob Moore
22295abccb5SBob Moore /*
22395abccb5SBob Moore * Descend down the namespace tree, collecting PCI device, function,
22495abccb5SBob Moore * and bus numbers. bus_number is only important for PCI bridges.
22595abccb5SBob Moore * Algorithm: As we descend the tree, use the last valid PCI device,
22695abccb5SBob Moore * function, and bus numbers that are discovered, and assign them
22795abccb5SBob Moore * to the PCI ID for the target device.
22895abccb5SBob Moore */
22995abccb5SBob Moore info = list_head;
23095abccb5SBob Moore while (info) {
23195abccb5SBob Moore status = acpi_hw_get_pci_device_info(pci_id, info->device,
23295abccb5SBob Moore &bus_number, &is_bridge);
23395abccb5SBob Moore if (ACPI_FAILURE(status)) {
23468aafc35SBob Moore return (status);
23595abccb5SBob Moore }
23695abccb5SBob Moore
23795abccb5SBob Moore info = info->next;
23895abccb5SBob Moore }
23995abccb5SBob Moore
24095abccb5SBob Moore ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
24195abccb5SBob Moore "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
24295abccb5SBob Moore "Status %X BusNumber %X IsBridge %X\n",
24395abccb5SBob Moore pci_id->segment, pci_id->bus, pci_id->device,
24495abccb5SBob Moore pci_id->function, status, bus_number, is_bridge));
24595abccb5SBob Moore
24668aafc35SBob Moore return (AE_OK);
24795abccb5SBob Moore }
24895abccb5SBob Moore
24995abccb5SBob Moore /*******************************************************************************
25095abccb5SBob Moore *
25195abccb5SBob Moore * FUNCTION: acpi_hw_delete_pci_list
25295abccb5SBob Moore *
25395abccb5SBob Moore * PARAMETERS: list_head - Device list created by
25495abccb5SBob Moore * acpi_hw_build_pci_list
25595abccb5SBob Moore *
25695abccb5SBob Moore * RETURN: None
25795abccb5SBob Moore *
25895abccb5SBob Moore * DESCRIPTION: Free the entire PCI list.
25995abccb5SBob Moore *
26095abccb5SBob Moore ******************************************************************************/
26195abccb5SBob Moore
acpi_hw_delete_pci_list(struct acpi_pci_device * list_head)26295abccb5SBob Moore static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)
26395abccb5SBob Moore {
26495abccb5SBob Moore struct acpi_pci_device *next;
26595abccb5SBob Moore struct acpi_pci_device *previous;
26695abccb5SBob Moore
26795abccb5SBob Moore next = list_head;
26895abccb5SBob Moore while (next) {
26995abccb5SBob Moore previous = next;
27095abccb5SBob Moore next = previous->next;
27195abccb5SBob Moore ACPI_FREE(previous);
27295abccb5SBob Moore }
27395abccb5SBob Moore }
27495abccb5SBob Moore
27595abccb5SBob Moore /*******************************************************************************
27695abccb5SBob Moore *
27795abccb5SBob Moore * FUNCTION: acpi_hw_get_pci_device_info
27895abccb5SBob Moore *
27995abccb5SBob Moore * PARAMETERS: pci_id - Initial values for the PCI ID. May be
28095abccb5SBob Moore * modified by this function.
28195abccb5SBob Moore * pci_device - Handle for the PCI device object
28295abccb5SBob Moore * bus_number - Where a PCI bridge bus number is returned
28395abccb5SBob Moore * is_bridge - Return value, indicates if this PCI
28495abccb5SBob Moore * device is a PCI bridge
28595abccb5SBob Moore *
28695abccb5SBob Moore * RETURN: Status
28795abccb5SBob Moore *
28895abccb5SBob Moore * DESCRIPTION: Get the device info for a single PCI device object. Get the
28995abccb5SBob Moore * _ADR (contains PCI device and function numbers), and for PCI
29095abccb5SBob Moore * bridge devices, get the bus number from PCI configuration
29195abccb5SBob Moore * space.
29295abccb5SBob Moore *
29395abccb5SBob Moore ******************************************************************************/
29495abccb5SBob Moore
29595abccb5SBob Moore static acpi_status
acpi_hw_get_pci_device_info(struct acpi_pci_id * pci_id,acpi_handle pci_device,u16 * bus_number,u8 * is_bridge)29695abccb5SBob Moore acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
29795abccb5SBob Moore acpi_handle pci_device,
29895abccb5SBob Moore u16 *bus_number, u8 *is_bridge)
29995abccb5SBob Moore {
30095abccb5SBob Moore acpi_status status;
30195abccb5SBob Moore acpi_object_type object_type;
30295abccb5SBob Moore u64 return_value;
30395abccb5SBob Moore u64 pci_value;
30495abccb5SBob Moore
30595abccb5SBob Moore /* We only care about objects of type Device */
30695abccb5SBob Moore
30795abccb5SBob Moore status = acpi_get_type(pci_device, &object_type);
30895abccb5SBob Moore if (ACPI_FAILURE(status)) {
30995abccb5SBob Moore return (status);
31095abccb5SBob Moore }
31195abccb5SBob Moore
31295abccb5SBob Moore if (object_type != ACPI_TYPE_DEVICE) {
31395abccb5SBob Moore return (AE_OK);
31495abccb5SBob Moore }
31595abccb5SBob Moore
31695abccb5SBob Moore /* We need an _ADR. Ignore device if not present */
31795abccb5SBob Moore
31895abccb5SBob Moore status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
31995abccb5SBob Moore pci_device, &return_value);
32095abccb5SBob Moore if (ACPI_FAILURE(status)) {
32195abccb5SBob Moore return (AE_OK);
32295abccb5SBob Moore }
32395abccb5SBob Moore
32495abccb5SBob Moore /*
32595abccb5SBob Moore * From _ADR, get the PCI Device and Function and
32695abccb5SBob Moore * update the PCI ID.
32795abccb5SBob Moore */
32895abccb5SBob Moore pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));
32995abccb5SBob Moore pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value));
33095abccb5SBob Moore
33195abccb5SBob Moore /*
33295abccb5SBob Moore * If the previous device was a bridge, use the previous
33395abccb5SBob Moore * device bus number
33495abccb5SBob Moore */
33595abccb5SBob Moore if (*is_bridge) {
33695abccb5SBob Moore pci_id->bus = *bus_number;
33795abccb5SBob Moore }
33895abccb5SBob Moore
33995abccb5SBob Moore /*
34095abccb5SBob Moore * Get the bus numbers from PCI Config space:
34195abccb5SBob Moore *
34295abccb5SBob Moore * First, get the PCI header_type
34395abccb5SBob Moore */
34495abccb5SBob Moore *is_bridge = FALSE;
34595abccb5SBob Moore status = acpi_os_read_pci_configuration(pci_id,
34695abccb5SBob Moore PCI_CFG_HEADER_TYPE_REG,
34795abccb5SBob Moore &pci_value, 8);
34895abccb5SBob Moore if (ACPI_FAILURE(status)) {
34995abccb5SBob Moore return (status);
35095abccb5SBob Moore }
35195abccb5SBob Moore
35295abccb5SBob Moore /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */
35395abccb5SBob Moore
35495abccb5SBob Moore pci_value &= PCI_HEADER_TYPE_MASK;
35595abccb5SBob Moore
35695abccb5SBob Moore if ((pci_value != PCI_TYPE_BRIDGE) &&
35795abccb5SBob Moore (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) {
35895abccb5SBob Moore return (AE_OK);
35995abccb5SBob Moore }
36095abccb5SBob Moore
36195abccb5SBob Moore /* Bridge: Get the Primary bus_number */
36295abccb5SBob Moore
36395abccb5SBob Moore status = acpi_os_read_pci_configuration(pci_id,
36495abccb5SBob Moore PCI_CFG_PRIMARY_BUS_NUMBER_REG,
36595abccb5SBob Moore &pci_value, 8);
36695abccb5SBob Moore if (ACPI_FAILURE(status)) {
36795abccb5SBob Moore return (status);
36895abccb5SBob Moore }
36995abccb5SBob Moore
37095abccb5SBob Moore *is_bridge = TRUE;
37195abccb5SBob Moore pci_id->bus = (u16)pci_value;
37295abccb5SBob Moore
37395abccb5SBob Moore /* Bridge: Get the Secondary bus_number */
37495abccb5SBob Moore
37595abccb5SBob Moore status = acpi_os_read_pci_configuration(pci_id,
37695abccb5SBob Moore PCI_CFG_SECONDARY_BUS_NUMBER_REG,
37795abccb5SBob Moore &pci_value, 8);
37895abccb5SBob Moore if (ACPI_FAILURE(status)) {
37995abccb5SBob Moore return (status);
38095abccb5SBob Moore }
38195abccb5SBob Moore
38295abccb5SBob Moore *bus_number = (u16)pci_value;
38395abccb5SBob Moore return (AE_OK);
38495abccb5SBob Moore }
385