brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 3bd5ebc Raw
385 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/*******************************************************************************3 *4 * Module Name: hwpci - Obtain PCI bus, device, and function numbers5 *6 ******************************************************************************/7 8#include <acpi/acpi.h>9#include "accommon.h"10 11#define _COMPONENT          ACPI_NAMESPACE12ACPI_MODULE_NAME("hwpci")13 14/* PCI configuration space values */15#define PCI_CFG_HEADER_TYPE_REG             0x0E16#define PCI_CFG_PRIMARY_BUS_NUMBER_REG      0x1817#define PCI_CFG_SECONDARY_BUS_NUMBER_REG    0x1918/* PCI header values */19#define PCI_HEADER_TYPE_MASK                0x7F20#define PCI_TYPE_BRIDGE                     0x0121#define PCI_TYPE_CARDBUS_BRIDGE             0x0222typedef struct acpi_pci_device {23	acpi_handle device;24	struct acpi_pci_device *next;25 26} acpi_pci_device;27 28/* Local prototypes */29 30static acpi_status31acpi_hw_build_pci_list(acpi_handle root_pci_device,32		       acpi_handle pci_region,33		       struct acpi_pci_device **return_list_head);34 35static acpi_status36acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,37			 struct acpi_pci_device *list_head);38 39static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head);40 41static acpi_status42acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,43			    acpi_handle pci_device,44			    u16 *bus_number, u8 *is_bridge);45 46/*******************************************************************************47 *48 * FUNCTION:    acpi_hw_derive_pci_id49 *50 * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be51 *                                    modified by this function.52 *              root_pci_device     - A handle to a PCI device object. This53 *                                    object must be a PCI Root Bridge having a54 *                                    _HID value of either PNP0A03 or PNP0A0855 *              pci_region          - A handle to a PCI configuration space56 *                                    Operation Region being initialized57 *58 * RETURN:      Status59 *60 * DESCRIPTION: This function derives a full PCI ID for a PCI device,61 *              consisting of a Segment number, Bus number, Device number,62 *              and function code.63 *64 *              The PCI hardware dynamically configures PCI bus numbers65 *              depending on the bus topology discovered during system66 *              initialization. This function is invoked during configuration67 *              of a PCI_Config Operation Region in order to (possibly) update68 *              the Bus/Device/Function numbers in the pci_id with the actual69 *              values as determined by the hardware and operating system70 *              configuration.71 *72 *              The pci_id parameter is initially populated during the Operation73 *              Region initialization. This function is then called, and is74 *              will make any necessary modifications to the Bus, Device, or75 *              Function number PCI ID subfields as appropriate for the76 *              current hardware and OS configuration.77 *78 * NOTE:        Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id79 *              interface since this feature is OS-independent. This module80 *              specifically avoids any use of recursion by building a local81 *              temporary device list.82 *83 ******************************************************************************/84 85acpi_status86acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,87		      acpi_handle root_pci_device, acpi_handle pci_region)88{89	acpi_status status;90	struct acpi_pci_device *list_head;91 92	ACPI_FUNCTION_TRACE(hw_derive_pci_id);93 94	if (!pci_id) {95		return_ACPI_STATUS(AE_BAD_PARAMETER);96	}97 98	/* Build a list of PCI devices, from pci_region up to root_pci_device */99 100	status =101	    acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);102	if (ACPI_SUCCESS(status)) {103 104		/* Walk the list, updating the PCI device/function/bus numbers */105 106		status = acpi_hw_process_pci_list(pci_id, list_head);107 108		/* Delete the list */109 110		acpi_hw_delete_pci_list(list_head);111	}112 113	return_ACPI_STATUS(status);114}115 116/*******************************************************************************117 *118 * FUNCTION:    acpi_hw_build_pci_list119 *120 * PARAMETERS:  root_pci_device     - A handle to a PCI device object. This121 *                                    object is guaranteed to be a PCI Root122 *                                    Bridge having a _HID value of either123 *                                    PNP0A03 or PNP0A08124 *              pci_region          - A handle to the PCI configuration space125 *                                    Operation Region126 *              return_list_head    - Where the PCI device list is returned127 *128 * RETURN:      Status129 *130 * DESCRIPTION: Builds a list of devices from the input PCI region up to the131 *              Root PCI device for this namespace subtree.132 *133 ******************************************************************************/134 135static acpi_status136acpi_hw_build_pci_list(acpi_handle root_pci_device,137		       acpi_handle pci_region,138		       struct acpi_pci_device **return_list_head)139{140	acpi_handle current_device;141	acpi_handle parent_device;142	acpi_status status;143	struct acpi_pci_device *list_element;144 145	/*146	 * Ascend namespace branch until the root_pci_device is reached, building147	 * a list of device nodes. Loop will exit when either the PCI device is148	 * found, or the root of the namespace is reached.149	 */150	*return_list_head = NULL;151	current_device = pci_region;152	while (1) {153		status = acpi_get_parent(current_device, &parent_device);154		if (ACPI_FAILURE(status)) {155 156			/* Must delete the list before exit */157 158			acpi_hw_delete_pci_list(*return_list_head);159			return (status);160		}161 162		/* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */163 164		if (parent_device == root_pci_device) {165			return (AE_OK);166		}167 168		list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));169		if (!list_element) {170 171			/* Must delete the list before exit */172 173			acpi_hw_delete_pci_list(*return_list_head);174			return (AE_NO_MEMORY);175		}176 177		/* Put new element at the head of the list */178 179		list_element->next = *return_list_head;180		list_element->device = parent_device;181		*return_list_head = list_element;182 183		current_device = parent_device;184	}185}186 187/*******************************************************************************188 *189 * FUNCTION:    acpi_hw_process_pci_list190 *191 * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be192 *                                    modified by this function.193 *              list_head           - Device list created by194 *                                    acpi_hw_build_pci_list195 *196 * RETURN:      Status197 *198 * DESCRIPTION: Walk downward through the PCI device list, getting the device199 *              info for each, via the PCI configuration space and updating200 *              the PCI ID as necessary. Deletes the list during traversal.201 *202 ******************************************************************************/203 204static acpi_status205acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,206			 struct acpi_pci_device *list_head)207{208	acpi_status status = AE_OK;209	struct acpi_pci_device *info;210	u16 bus_number;211	u8 is_bridge = TRUE;212 213	ACPI_FUNCTION_NAME(hw_process_pci_list);214 215	ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,216			  "Input PciId:  Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",217			  pci_id->segment, pci_id->bus, pci_id->device,218			  pci_id->function));219 220	bus_number = pci_id->bus;221 222	/*223	 * Descend down the namespace tree, collecting PCI device, function,224	 * and bus numbers. bus_number is only important for PCI bridges.225	 * Algorithm: As we descend the tree, use the last valid PCI device,226	 * function, and bus numbers that are discovered, and assign them227	 * to the PCI ID for the target device.228	 */229	info = list_head;230	while (info) {231		status = acpi_hw_get_pci_device_info(pci_id, info->device,232						     &bus_number, &is_bridge);233		if (ACPI_FAILURE(status)) {234			return (status);235		}236 237		info = info->next;238	}239 240	ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,241			  "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "242			  "Status %X BusNumber %X IsBridge %X\n",243			  pci_id->segment, pci_id->bus, pci_id->device,244			  pci_id->function, status, bus_number, is_bridge));245 246	return (AE_OK);247}248 249/*******************************************************************************250 *251 * FUNCTION:    acpi_hw_delete_pci_list252 *253 * PARAMETERS:  list_head           - Device list created by254 *                                    acpi_hw_build_pci_list255 *256 * RETURN:      None257 *258 * DESCRIPTION: Free the entire PCI list.259 *260 ******************************************************************************/261 262static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)263{264	struct acpi_pci_device *next;265	struct acpi_pci_device *previous;266 267	next = list_head;268	while (next) {269		previous = next;270		next = previous->next;271		ACPI_FREE(previous);272	}273}274 275/*******************************************************************************276 *277 * FUNCTION:    acpi_hw_get_pci_device_info278 *279 * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be280 *                                    modified by this function.281 *              pci_device          - Handle for the PCI device object282 *              bus_number          - Where a PCI bridge bus number is returned283 *              is_bridge           - Return value, indicates if this PCI284 *                                    device is a PCI bridge285 *286 * RETURN:      Status287 *288 * DESCRIPTION: Get the device info for a single PCI device object. Get the289 *              _ADR (contains PCI device and function numbers), and for PCI290 *              bridge devices, get the bus number from PCI configuration291 *              space.292 *293 ******************************************************************************/294 295static acpi_status296acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,297			    acpi_handle pci_device,298			    u16 *bus_number, u8 *is_bridge)299{300	acpi_status status;301	acpi_object_type object_type;302	u64 return_value;303	u64 pci_value;304 305	/* We only care about objects of type Device */306 307	status = acpi_get_type(pci_device, &object_type);308	if (ACPI_FAILURE(status)) {309		return (status);310	}311 312	if (object_type != ACPI_TYPE_DEVICE) {313		return (AE_OK);314	}315 316	/* We need an _ADR. Ignore device if not present */317 318	status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,319						 pci_device, &return_value);320	if (ACPI_FAILURE(status)) {321		return (AE_OK);322	}323 324	/*325	 * From _ADR, get the PCI Device and Function and326	 * update the PCI ID.327	 */328	pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));329	pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value));330 331	/*332	 * If the previous device was a bridge, use the previous333	 * device bus number334	 */335	if (*is_bridge) {336		pci_id->bus = *bus_number;337	}338 339	/*340	 * Get the bus numbers from PCI Config space:341	 *342	 * First, get the PCI header_type343	 */344	*is_bridge = FALSE;345	status = acpi_os_read_pci_configuration(pci_id,346						PCI_CFG_HEADER_TYPE_REG,347						&pci_value, 8);348	if (ACPI_FAILURE(status)) {349		return (status);350	}351 352	/* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */353 354	pci_value &= PCI_HEADER_TYPE_MASK;355 356	if ((pci_value != PCI_TYPE_BRIDGE) &&357	    (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) {358		return (AE_OK);359	}360 361	/* Bridge: Get the Primary bus_number */362 363	status = acpi_os_read_pci_configuration(pci_id,364						PCI_CFG_PRIMARY_BUS_NUMBER_REG,365						&pci_value, 8);366	if (ACPI_FAILURE(status)) {367		return (status);368	}369 370	*is_bridge = TRUE;371	pci_id->bus = (u16)pci_value;372 373	/* Bridge: Get the Secondary bus_number */374 375	status = acpi_os_read_pci_configuration(pci_id,376						PCI_CFG_SECONDARY_BUS_NUMBER_REG,377						&pci_value, 8);378	if (ACPI_FAILURE(status)) {379		return (status);380	}381 382	*bus_number = (u16)pci_value;383	return (AE_OK);384}385