1 /******************************************************************************* 2 * 3 * Module Name: hwpci - Obtain PCI bus, device, and function numbers 4 * 5 ******************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2014, 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 MERCHANTIBILITY 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 #define __HWPCI_C__ 45 46 #include <contrib/dev/acpica/include/acpi.h> 47 #include <contrib/dev/acpica/include/accommon.h> 48 49 50 #define _COMPONENT ACPI_NAMESPACE 51 ACPI_MODULE_NAME ("hwpci") 52 53 54 /* PCI configuration space values */ 55 56 #define PCI_CFG_HEADER_TYPE_REG 0x0E 57 #define PCI_CFG_PRIMARY_BUS_NUMBER_REG 0x18 58 #define PCI_CFG_SECONDARY_BUS_NUMBER_REG 0x19 59 60 /* PCI header values */ 61 62 #define PCI_HEADER_TYPE_MASK 0x7F 63 #define PCI_TYPE_BRIDGE 0x01 64 #define PCI_TYPE_CARDBUS_BRIDGE 0x02 65 66 typedef struct acpi_pci_device 67 { 68 ACPI_HANDLE Device; 69 struct acpi_pci_device *Next; 70 71 } ACPI_PCI_DEVICE; 72 73 74 /* Local prototypes */ 75 76 static ACPI_STATUS 77 AcpiHwBuildPciList ( 78 ACPI_HANDLE RootPciDevice, 79 ACPI_HANDLE PciRegion, 80 ACPI_PCI_DEVICE **ReturnListHead); 81 82 static ACPI_STATUS 83 AcpiHwProcessPciList ( 84 ACPI_PCI_ID *PciId, 85 ACPI_PCI_DEVICE *ListHead); 86 87 static void 88 AcpiHwDeletePciList ( 89 ACPI_PCI_DEVICE *ListHead); 90 91 static ACPI_STATUS 92 AcpiHwGetPciDeviceInfo ( 93 ACPI_PCI_ID *PciId, 94 ACPI_HANDLE PciDevice, 95 UINT16 *BusNumber, 96 BOOLEAN *IsBridge); 97 98 99 /******************************************************************************* 100 * 101 * FUNCTION: AcpiHwDerivePciId 102 * 103 * PARAMETERS: PciId - Initial values for the PCI ID. May be 104 * modified by this function. 105 * RootPciDevice - A handle to a PCI device object. This 106 * object must be a PCI Root Bridge having a 107 * _HID value of either PNP0A03 or PNP0A08 108 * PciRegion - A handle to a PCI configuration space 109 * Operation Region being initialized 110 * 111 * RETURN: Status 112 * 113 * DESCRIPTION: This function derives a full PCI ID for a PCI device, 114 * consisting of a Segment number, Bus number, Device number, 115 * and function code. 116 * 117 * The PCI hardware dynamically configures PCI bus numbers 118 * depending on the bus topology discovered during system 119 * initialization. This function is invoked during configuration 120 * of a PCI_Config Operation Region in order to (possibly) update 121 * the Bus/Device/Function numbers in the PciId with the actual 122 * values as determined by the hardware and operating system 123 * configuration. 124 * 125 * The PciId parameter is initially populated during the Operation 126 * Region initialization. This function is then called, and is 127 * will make any necessary modifications to the Bus, Device, or 128 * Function number PCI ID subfields as appropriate for the 129 * current hardware and OS configuration. 130 * 131 * NOTE: Created 08/2010. Replaces the previous OSL AcpiOsDerivePciId 132 * interface since this feature is OS-independent. This module 133 * specifically avoids any use of recursion by building a local 134 * temporary device list. 135 * 136 ******************************************************************************/ 137 138 ACPI_STATUS 139 AcpiHwDerivePciId ( 140 ACPI_PCI_ID *PciId, 141 ACPI_HANDLE RootPciDevice, 142 ACPI_HANDLE PciRegion) 143 { 144 ACPI_STATUS Status; 145 ACPI_PCI_DEVICE *ListHead = NULL; 146 147 148 ACPI_FUNCTION_TRACE (HwDerivePciId); 149 150 151 if (!PciId) 152 { 153 return_ACPI_STATUS (AE_BAD_PARAMETER); 154 } 155 156 /* Build a list of PCI devices, from PciRegion up to RootPciDevice */ 157 158 Status = AcpiHwBuildPciList (RootPciDevice, PciRegion, &ListHead); 159 if (ACPI_SUCCESS (Status)) 160 { 161 /* Walk the list, updating the PCI device/function/bus numbers */ 162 163 Status = AcpiHwProcessPciList (PciId, ListHead); 164 165 /* Delete the list */ 166 167 AcpiHwDeletePciList (ListHead); 168 } 169 170 return_ACPI_STATUS (Status); 171 } 172 173 174 /******************************************************************************* 175 * 176 * FUNCTION: AcpiHwBuildPciList 177 * 178 * PARAMETERS: RootPciDevice - A handle to a PCI device object. This 179 * object is guaranteed to be a PCI Root 180 * Bridge having a _HID value of either 181 * PNP0A03 or PNP0A08 182 * PciRegion - A handle to the PCI configuration space 183 * Operation Region 184 * ReturnListHead - Where the PCI device list is returned 185 * 186 * RETURN: Status 187 * 188 * DESCRIPTION: Builds a list of devices from the input PCI region up to the 189 * Root PCI device for this namespace subtree. 190 * 191 ******************************************************************************/ 192 193 static ACPI_STATUS 194 AcpiHwBuildPciList ( 195 ACPI_HANDLE RootPciDevice, 196 ACPI_HANDLE PciRegion, 197 ACPI_PCI_DEVICE **ReturnListHead) 198 { 199 ACPI_HANDLE CurrentDevice; 200 ACPI_HANDLE ParentDevice; 201 ACPI_STATUS Status; 202 ACPI_PCI_DEVICE *ListElement; 203 ACPI_PCI_DEVICE *ListHead = NULL; 204 205 206 /* 207 * Ascend namespace branch until the RootPciDevice is reached, building 208 * a list of device nodes. Loop will exit when either the PCI device is 209 * found, or the root of the namespace is reached. 210 */ 211 CurrentDevice = PciRegion; 212 while (1) 213 { 214 Status = AcpiGetParent (CurrentDevice, &ParentDevice); 215 if (ACPI_FAILURE (Status)) 216 { 217 /* Must delete the list before exit */ 218 219 AcpiHwDeletePciList (*ReturnListHead); 220 return (Status); 221 } 222 223 /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */ 224 225 if (ParentDevice == RootPciDevice) 226 { 227 *ReturnListHead = ListHead; 228 return (AE_OK); 229 } 230 231 ListElement = ACPI_ALLOCATE (sizeof (ACPI_PCI_DEVICE)); 232 if (!ListElement) 233 { 234 /* Must delete the list before exit */ 235 236 AcpiHwDeletePciList (*ReturnListHead); 237 return (AE_NO_MEMORY); 238 } 239 240 /* Put new element at the head of the list */ 241 242 ListElement->Next = ListHead; 243 ListElement->Device = ParentDevice; 244 ListHead = ListElement; 245 246 CurrentDevice = ParentDevice; 247 } 248 } 249 250 251 /******************************************************************************* 252 * 253 * FUNCTION: AcpiHwProcessPciList 254 * 255 * PARAMETERS: PciId - Initial values for the PCI ID. May be 256 * modified by this function. 257 * ListHead - Device list created by 258 * AcpiHwBuildPciList 259 * 260 * RETURN: Status 261 * 262 * DESCRIPTION: Walk downward through the PCI device list, getting the device 263 * info for each, via the PCI configuration space and updating 264 * the PCI ID as necessary. Deletes the list during traversal. 265 * 266 ******************************************************************************/ 267 268 static ACPI_STATUS 269 AcpiHwProcessPciList ( 270 ACPI_PCI_ID *PciId, 271 ACPI_PCI_DEVICE *ListHead) 272 { 273 ACPI_STATUS Status = AE_OK; 274 ACPI_PCI_DEVICE *Info; 275 UINT16 BusNumber; 276 BOOLEAN IsBridge = TRUE; 277 278 279 ACPI_FUNCTION_NAME (HwProcessPciList); 280 281 282 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, 283 "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n", 284 PciId->Segment, PciId->Bus, PciId->Device, PciId->Function)); 285 286 BusNumber = PciId->Bus; 287 288 /* 289 * Descend down the namespace tree, collecting PCI device, function, 290 * and bus numbers. BusNumber is only important for PCI bridges. 291 * Algorithm: As we descend the tree, use the last valid PCI device, 292 * function, and bus numbers that are discovered, and assign them 293 * to the PCI ID for the target device. 294 */ 295 Info = ListHead; 296 while (Info) 297 { 298 Status = AcpiHwGetPciDeviceInfo (PciId, Info->Device, 299 &BusNumber, &IsBridge); 300 if (ACPI_FAILURE (Status)) 301 { 302 return (Status); 303 } 304 305 Info = Info->Next; 306 } 307 308 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, 309 "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X " 310 "Status %X BusNumber %X IsBridge %X\n", 311 PciId->Segment, PciId->Bus, PciId->Device, PciId->Function, 312 Status, BusNumber, IsBridge)); 313 314 return (AE_OK); 315 } 316 317 318 /******************************************************************************* 319 * 320 * FUNCTION: AcpiHwDeletePciList 321 * 322 * PARAMETERS: ListHead - Device list created by 323 * AcpiHwBuildPciList 324 * 325 * RETURN: None 326 * 327 * DESCRIPTION: Free the entire PCI list. 328 * 329 ******************************************************************************/ 330 331 static void 332 AcpiHwDeletePciList ( 333 ACPI_PCI_DEVICE *ListHead) 334 { 335 ACPI_PCI_DEVICE *Next; 336 ACPI_PCI_DEVICE *Previous; 337 338 339 Next = ListHead; 340 while (Next) 341 { 342 Previous = Next; 343 Next = Previous->Next; 344 ACPI_FREE (Previous); 345 } 346 } 347 348 349 /******************************************************************************* 350 * 351 * FUNCTION: AcpiHwGetPciDeviceInfo 352 * 353 * PARAMETERS: PciId - Initial values for the PCI ID. May be 354 * modified by this function. 355 * PciDevice - Handle for the PCI device object 356 * BusNumber - Where a PCI bridge bus number is returned 357 * IsBridge - Return value, indicates if this PCI 358 * device is a PCI bridge 359 * 360 * RETURN: Status 361 * 362 * DESCRIPTION: Get the device info for a single PCI device object. Get the 363 * _ADR (contains PCI device and function numbers), and for PCI 364 * bridge devices, get the bus number from PCI configuration 365 * space. 366 * 367 ******************************************************************************/ 368 369 static ACPI_STATUS 370 AcpiHwGetPciDeviceInfo ( 371 ACPI_PCI_ID *PciId, 372 ACPI_HANDLE PciDevice, 373 UINT16 *BusNumber, 374 BOOLEAN *IsBridge) 375 { 376 ACPI_STATUS Status; 377 ACPI_OBJECT_TYPE ObjectType; 378 UINT64 ReturnValue; 379 UINT64 PciValue; 380 381 382 /* We only care about objects of type Device */ 383 384 Status = AcpiGetType (PciDevice, &ObjectType); 385 if (ACPI_FAILURE (Status)) 386 { 387 return (Status); 388 } 389 390 if (ObjectType != ACPI_TYPE_DEVICE) 391 { 392 return (AE_OK); 393 } 394 395 /* We need an _ADR. Ignore device if not present */ 396 397 Status = AcpiUtEvaluateNumericObject (METHOD_NAME__ADR, 398 PciDevice, &ReturnValue); 399 if (ACPI_FAILURE (Status)) 400 { 401 return (AE_OK); 402 } 403 404 /* 405 * From _ADR, get the PCI Device and Function and 406 * update the PCI ID. 407 */ 408 PciId->Device = ACPI_HIWORD (ACPI_LODWORD (ReturnValue)); 409 PciId->Function = ACPI_LOWORD (ACPI_LODWORD (ReturnValue)); 410 411 /* 412 * If the previous device was a bridge, use the previous 413 * device bus number 414 */ 415 if (*IsBridge) 416 { 417 PciId->Bus = *BusNumber; 418 } 419 420 /* 421 * Get the bus numbers from PCI Config space: 422 * 423 * First, get the PCI HeaderType 424 */ 425 *IsBridge = FALSE; 426 Status = AcpiOsReadPciConfiguration (PciId, 427 PCI_CFG_HEADER_TYPE_REG, &PciValue, 8); 428 if (ACPI_FAILURE (Status)) 429 { 430 return (Status); 431 } 432 433 /* We only care about bridges (1=PciBridge, 2=CardBusBridge) */ 434 435 PciValue &= PCI_HEADER_TYPE_MASK; 436 437 if ((PciValue != PCI_TYPE_BRIDGE) && 438 (PciValue != PCI_TYPE_CARDBUS_BRIDGE)) 439 { 440 return (AE_OK); 441 } 442 443 /* Bridge: Get the Primary BusNumber */ 444 445 Status = AcpiOsReadPciConfiguration (PciId, 446 PCI_CFG_PRIMARY_BUS_NUMBER_REG, &PciValue, 8); 447 if (ACPI_FAILURE (Status)) 448 { 449 return (Status); 450 } 451 452 *IsBridge = TRUE; 453 PciId->Bus = (UINT16) PciValue; 454 455 /* Bridge: Get the Secondary BusNumber */ 456 457 Status = AcpiOsReadPciConfiguration (PciId, 458 PCI_CFG_SECONDARY_BUS_NUMBER_REG, &PciValue, 8); 459 if (ACPI_FAILURE (Status)) 460 { 461 return (Status); 462 } 463 464 *BusNumber = (UINT16) PciValue; 465 return (AE_OK); 466 } 467