| 1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 | 
|---|
| 2 | /******************************************************************************* | 
|---|
| 3 | * | 
|---|
| 4 | * Module Name: hwpci - Obtain PCI bus, device, and function numbers | 
|---|
| 5 | * | 
|---|
| 6 | ******************************************************************************/ | 
|---|
| 7 |  | 
|---|
| 8 | #include <acpi/acpi.h> | 
|---|
| 9 | #include "accommon.h" | 
|---|
| 10 |  | 
|---|
| 11 | #define _COMPONENT          ACPI_NAMESPACE | 
|---|
| 12 | ACPI_MODULE_NAME( "hwpci") | 
|---|
| 13 |  | 
|---|
| 14 | /* PCI configuration space values */ | 
|---|
| 15 | #define              0x0E | 
|---|
| 16 | #define PCI_CFG_PRIMARY_BUS_NUMBER_REG      0x18 | 
|---|
| 17 | #define PCI_CFG_SECONDARY_BUS_NUMBER_REG    0x19 | 
|---|
| 18 | /* PCI header values */ | 
|---|
| 19 | #define                 0x7F | 
|---|
| 20 | #define PCI_TYPE_BRIDGE                     0x01 | 
|---|
| 21 | #define PCI_TYPE_CARDBUS_BRIDGE             0x02 | 
|---|
| 22 | typedef struct acpi_pci_device { | 
|---|
| 23 | acpi_handle device; | 
|---|
| 24 | struct acpi_pci_device *next; | 
|---|
| 25 |  | 
|---|
| 26 | } acpi_pci_device; | 
|---|
| 27 |  | 
|---|
| 28 | /* Local prototypes */ | 
|---|
| 29 |  | 
|---|
| 30 | static acpi_status | 
|---|
| 31 | acpi_hw_build_pci_list(acpi_handle root_pci_device, | 
|---|
| 32 | acpi_handle pci_region, | 
|---|
| 33 | struct acpi_pci_device **return_list_head); | 
|---|
| 34 |  | 
|---|
| 35 | static acpi_status | 
|---|
| 36 | acpi_hw_process_pci_list(struct acpi_pci_id *pci_id, | 
|---|
| 37 | struct acpi_pci_device *list_head); | 
|---|
| 38 |  | 
|---|
| 39 | static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head); | 
|---|
| 40 |  | 
|---|
| 41 | static acpi_status | 
|---|
| 42 | acpi_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_id | 
|---|
| 49 | * | 
|---|
| 50 | * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be | 
|---|
| 51 | *                                    modified by this function. | 
|---|
| 52 | *              root_pci_device     - A handle to a PCI device object. This | 
|---|
| 53 | *                                    object must be a PCI Root Bridge having a | 
|---|
| 54 | *                                    _HID value of either PNP0A03 or PNP0A08 | 
|---|
| 55 | *              pci_region          - A handle to a PCI configuration space | 
|---|
| 56 | *                                    Operation Region being initialized | 
|---|
| 57 | * | 
|---|
| 58 | * RETURN:      Status | 
|---|
| 59 | * | 
|---|
| 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 numbers | 
|---|
| 65 | *              depending on the bus topology discovered during system | 
|---|
| 66 | *              initialization. This function is invoked during configuration | 
|---|
| 67 | *              of a PCI_Config Operation Region in order to (possibly) update | 
|---|
| 68 | *              the Bus/Device/Function numbers in the pci_id with the actual | 
|---|
| 69 | *              values as determined by the hardware and operating system | 
|---|
| 70 | *              configuration. | 
|---|
| 71 | * | 
|---|
| 72 | *              The pci_id parameter is initially populated during the Operation | 
|---|
| 73 | *              Region initialization. This function is then called, and is | 
|---|
| 74 | *              will make any necessary modifications to the Bus, Device, or | 
|---|
| 75 | *              Function number PCI ID subfields as appropriate for the | 
|---|
| 76 | *              current hardware and OS configuration. | 
|---|
| 77 | * | 
|---|
| 78 | * NOTE:        Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id | 
|---|
| 79 | *              interface since this feature is OS-independent. This module | 
|---|
| 80 | *              specifically avoids any use of recursion by building a local | 
|---|
| 81 | *              temporary device list. | 
|---|
| 82 | * | 
|---|
| 83 | ******************************************************************************/ | 
|---|
| 84 |  | 
|---|
| 85 | acpi_status | 
|---|
| 86 | acpi_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, return_list_head: &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_list | 
|---|
| 119 | * | 
|---|
| 120 | * PARAMETERS:  root_pci_device     - A handle to a PCI device object. This | 
|---|
| 121 | *                                    object is guaranteed to be a PCI Root | 
|---|
| 122 | *                                    Bridge having a _HID value of either | 
|---|
| 123 | *                                    PNP0A03 or PNP0A08 | 
|---|
| 124 | *              pci_region          - A handle to the PCI configuration space | 
|---|
| 125 | *                                    Operation Region | 
|---|
| 126 | *              return_list_head    - Where the PCI device list is returned | 
|---|
| 127 | * | 
|---|
| 128 | * RETURN:      Status | 
|---|
| 129 | * | 
|---|
| 130 | * DESCRIPTION: Builds a list of devices from the input PCI region up to the | 
|---|
| 131 | *              Root PCI device for this namespace subtree. | 
|---|
| 132 | * | 
|---|
| 133 | ******************************************************************************/ | 
|---|
| 134 |  | 
|---|
| 135 | static acpi_status | 
|---|
| 136 | acpi_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, building | 
|---|
| 147 | * a list of device nodes. Loop will exit when either the PCI device is | 
|---|
| 148 | * 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(object: current_device, out_handle: &parent_device); | 
|---|
| 154 | if (ACPI_FAILURE(status)) { | 
|---|
| 155 |  | 
|---|
| 156 | /* Must delete the list before exit */ | 
|---|
| 157 |  | 
|---|
| 158 | acpi_hw_delete_pci_list(list_head: *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(list_head: *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_list | 
|---|
| 190 | * | 
|---|
| 191 | * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be | 
|---|
| 192 | *                                    modified by this function. | 
|---|
| 193 | *              list_head           - Device list created by | 
|---|
| 194 | *                                    acpi_hw_build_pci_list | 
|---|
| 195 | * | 
|---|
| 196 | * RETURN:      Status | 
|---|
| 197 | * | 
|---|
| 198 | * DESCRIPTION: Walk downward through the PCI device list, getting the device | 
|---|
| 199 | *              info for each, via the PCI configuration space and updating | 
|---|
| 200 | *              the PCI ID as necessary. Deletes the list during traversal. | 
|---|
| 201 | * | 
|---|
| 202 | ******************************************************************************/ | 
|---|
| 203 |  | 
|---|
| 204 | static acpi_status | 
|---|
| 205 | acpi_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 them | 
|---|
| 227 | * 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, pci_device: info->device, | 
|---|
| 232 | bus_number: &bus_number, is_bridge: &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_list | 
|---|
| 252 | * | 
|---|
| 253 | * PARAMETERS:  list_head           - Device list created by | 
|---|
| 254 | *                                    acpi_hw_build_pci_list | 
|---|
| 255 | * | 
|---|
| 256 | * RETURN:      None | 
|---|
| 257 | * | 
|---|
| 258 | * DESCRIPTION: Free the entire PCI list. | 
|---|
| 259 | * | 
|---|
| 260 | ******************************************************************************/ | 
|---|
| 261 |  | 
|---|
| 262 | static 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_info | 
|---|
| 278 | * | 
|---|
| 279 | * PARAMETERS:  pci_id              - Initial values for the PCI ID. May be | 
|---|
| 280 | *                                    modified by this function. | 
|---|
| 281 | *              pci_device          - Handle for the PCI device object | 
|---|
| 282 | *              bus_number          - Where a PCI bridge bus number is returned | 
|---|
| 283 | *              is_bridge           - Return value, indicates if this PCI | 
|---|
| 284 | *                                    device is a PCI bridge | 
|---|
| 285 | * | 
|---|
| 286 | * RETURN:      Status | 
|---|
| 287 | * | 
|---|
| 288 | * DESCRIPTION: Get the device info for a single PCI device object. Get the | 
|---|
| 289 | *              _ADR (contains PCI device and function numbers), and for PCI | 
|---|
| 290 | *              bridge devices, get the bus number from PCI configuration | 
|---|
| 291 | *              space. | 
|---|
| 292 | * | 
|---|
| 293 | ******************************************************************************/ | 
|---|
| 294 |  | 
|---|
| 295 | static acpi_status | 
|---|
| 296 | acpi_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(object: pci_device, out_type: &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 | device_node: pci_device, value: &return_value); | 
|---|
| 320 | if (ACPI_FAILURE(status)) { | 
|---|
| 321 | return (AE_OK); | 
|---|
| 322 | } | 
|---|
| 323 |  | 
|---|
| 324 | /* | 
|---|
| 325 | * From _ADR, get the PCI Device and Function and | 
|---|
| 326 | * 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 previous | 
|---|
| 333 | * device bus number | 
|---|
| 334 | */ | 
|---|
| 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_type | 
|---|
| 343 | */ | 
|---|
| 344 | *is_bridge = FALSE; | 
|---|
| 345 | status = acpi_os_read_pci_configuration(pci_id, | 
|---|
| 346 | PCI_CFG_HEADER_TYPE_REG, | 
|---|
| 347 | value: &pci_value, width: 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 | value: &pci_value, width: 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 | value: &pci_value, width: 8); | 
|---|
| 378 | if (ACPI_FAILURE(status)) { | 
|---|
| 379 | return (status); | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | *bus_number = (u16)pci_value; | 
|---|
| 383 | return (AE_OK); | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|