1 /******************************************************************************* 2 * 3 * Module Name: hwpci - Obtain PCI bus, device, and function numbers 4 * 5 ******************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2013, 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 166 /* Always delete the list */ 167 168 AcpiHwDeletePciList (ListHead); 169 return_ACPI_STATUS (Status); 170 } 171 172 173 /******************************************************************************* 174 * 175 * FUNCTION: AcpiHwBuildPciList 176 * 177 * PARAMETERS: RootPciDevice - A handle to a PCI device object. This 178 * object is guaranteed to be a PCI Root 179 * Bridge having a _HID value of either 180 * PNP0A03 or PNP0A08 181 * PciRegion - A handle to the PCI configuration space 182 * Operation Region 183 * ReturnListHead - Where the PCI device list is returned 184 * 185 * RETURN: Status 186 * 187 * DESCRIPTION: Builds a list of devices from the input PCI region up to the 188 * Root PCI device for this namespace subtree. 189 * 190 ******************************************************************************/ 191 192 static ACPI_STATUS 193 AcpiHwBuildPciList ( 194 ACPI_HANDLE RootPciDevice, 195 ACPI_HANDLE PciRegion, 196 ACPI_PCI_DEVICE **ReturnListHead) 197 { 198 ACPI_HANDLE CurrentDevice; 199 ACPI_HANDLE ParentDevice; 200 ACPI_STATUS Status; 201 ACPI_PCI_DEVICE *ListElement; 202 ACPI_PCI_DEVICE *ListHead = NULL; 203 204 205 /* 206 * Ascend namespace branch until the RootPciDevice is reached, building 207 * a list of device nodes. Loop will exit when either the PCI device is 208 * found, or the root of the namespace is reached. 209 */ 210 CurrentDevice = PciRegion; 211 while (1) 212 { 213 Status = AcpiGetParent (CurrentDevice, &ParentDevice); 214 if (ACPI_FAILURE (Status)) 215 { 216 return (Status); 217 } 218 219 /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */ 220 221 if (ParentDevice == RootPciDevice) 222 { 223 *ReturnListHead = ListHead; 224 return (AE_OK); 225 } 226 227 ListElement = ACPI_ALLOCATE (sizeof (ACPI_PCI_DEVICE)); 228 if (!ListElement) 229 { 230 return (AE_NO_MEMORY); 231 } 232 233 /* Put new element at the head of the list */ 234 235 ListElement->Next = ListHead; 236 ListElement->Device = ParentDevice; 237 ListHead = ListElement; 238 239 CurrentDevice = ParentDevice; 240 } 241 } 242 243 244 /******************************************************************************* 245 * 246 * FUNCTION: AcpiHwProcessPciList 247 * 248 * PARAMETERS: PciId - Initial values for the PCI ID. May be 249 * modified by this function. 250 * ListHead - Device list created by 251 * AcpiHwBuildPciList 252 * 253 * RETURN: Status 254 * 255 * DESCRIPTION: Walk downward through the PCI device list, getting the device 256 * info for each, via the PCI configuration space and updating 257 * the PCI ID as necessary. Deletes the list during traversal. 258 * 259 ******************************************************************************/ 260 261 static ACPI_STATUS 262 AcpiHwProcessPciList ( 263 ACPI_PCI_ID *PciId, 264 ACPI_PCI_DEVICE *ListHead) 265 { 266 ACPI_STATUS Status = AE_OK; 267 ACPI_PCI_DEVICE *Info; 268 UINT16 BusNumber; 269 BOOLEAN IsBridge = TRUE; 270 271 272 ACPI_FUNCTION_NAME (HwProcessPciList); 273 274 275 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, 276 "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n", 277 PciId->Segment, PciId->Bus, PciId->Device, PciId->Function)); 278 279 BusNumber = PciId->Bus; 280 281 /* 282 * Descend down the namespace tree, collecting PCI device, function, 283 * and bus numbers. BusNumber is only important for PCI bridges. 284 * Algorithm: As we descend the tree, use the last valid PCI device, 285 * function, and bus numbers that are discovered, and assign them 286 * to the PCI ID for the target device. 287 */ 288 Info = ListHead; 289 while (Info) 290 { 291 Status = AcpiHwGetPciDeviceInfo (PciId, Info->Device, 292 &BusNumber, &IsBridge); 293 if (ACPI_FAILURE (Status)) 294 { 295 return (Status); 296 } 297 298 Info = Info->Next; 299 } 300 301 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, 302 "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X " 303 "Status %X BusNumber %X IsBridge %X\n", 304 PciId->Segment, PciId->Bus, PciId->Device, PciId->Function, 305 Status, BusNumber, IsBridge)); 306 307 return (AE_OK); 308 } 309 310 311 /******************************************************************************* 312 * 313 * FUNCTION: AcpiHwDeletePciList 314 * 315 * PARAMETERS: ListHead - Device list created by 316 * AcpiHwBuildPciList 317 * 318 * RETURN: None 319 * 320 * DESCRIPTION: Free the entire PCI list. 321 * 322 ******************************************************************************/ 323 324 static void 325 AcpiHwDeletePciList ( 326 ACPI_PCI_DEVICE *ListHead) 327 { 328 ACPI_PCI_DEVICE *Next; 329 ACPI_PCI_DEVICE *Previous; 330 331 332 Next = ListHead; 333 while (Next) 334 { 335 Previous = Next; 336 Next = Previous->Next; 337 ACPI_FREE (Previous); 338 } 339 } 340 341 342 /******************************************************************************* 343 * 344 * FUNCTION: AcpiHwGetPciDeviceInfo 345 * 346 * PARAMETERS: PciId - Initial values for the PCI ID. May be 347 * modified by this function. 348 * PciDevice - Handle for the PCI device object 349 * BusNumber - Where a PCI bridge bus number is returned 350 * IsBridge - Return value, indicates if this PCI 351 * device is a PCI bridge 352 * 353 * RETURN: Status 354 * 355 * DESCRIPTION: Get the device info for a single PCI device object. Get the 356 * _ADR (contains PCI device and function numbers), and for PCI 357 * bridge devices, get the bus number from PCI configuration 358 * space. 359 * 360 ******************************************************************************/ 361 362 static ACPI_STATUS 363 AcpiHwGetPciDeviceInfo ( 364 ACPI_PCI_ID *PciId, 365 ACPI_HANDLE PciDevice, 366 UINT16 *BusNumber, 367 BOOLEAN *IsBridge) 368 { 369 ACPI_STATUS Status; 370 ACPI_OBJECT_TYPE ObjectType; 371 UINT64 ReturnValue; 372 UINT64 PciValue; 373 374 375 /* We only care about objects of type Device */ 376 377 Status = AcpiGetType (PciDevice, &ObjectType); 378 if (ACPI_FAILURE (Status)) 379 { 380 return (Status); 381 } 382 383 if (ObjectType != ACPI_TYPE_DEVICE) 384 { 385 return (AE_OK); 386 } 387 388 /* We need an _ADR. Ignore device if not present */ 389 390 Status = AcpiUtEvaluateNumericObject (METHOD_NAME__ADR, 391 PciDevice, &ReturnValue); 392 if (ACPI_FAILURE (Status)) 393 { 394 return (AE_OK); 395 } 396 397 /* 398 * From _ADR, get the PCI Device and Function and 399 * update the PCI ID. 400 */ 401 PciId->Device = ACPI_HIWORD (ACPI_LODWORD (ReturnValue)); 402 PciId->Function = ACPI_LOWORD (ACPI_LODWORD (ReturnValue)); 403 404 /* 405 * If the previous device was a bridge, use the previous 406 * device bus number 407 */ 408 if (*IsBridge) 409 { 410 PciId->Bus = *BusNumber; 411 } 412 413 /* 414 * Get the bus numbers from PCI Config space: 415 * 416 * First, get the PCI HeaderType 417 */ 418 *IsBridge = FALSE; 419 Status = AcpiOsReadPciConfiguration (PciId, 420 PCI_CFG_HEADER_TYPE_REG, &PciValue, 8); 421 if (ACPI_FAILURE (Status)) 422 { 423 return (Status); 424 } 425 426 /* We only care about bridges (1=PciBridge, 2=CardBusBridge) */ 427 428 PciValue &= PCI_HEADER_TYPE_MASK; 429 430 if ((PciValue != PCI_TYPE_BRIDGE) && 431 (PciValue != PCI_TYPE_CARDBUS_BRIDGE)) 432 { 433 return (AE_OK); 434 } 435 436 /* Bridge: Get the Primary BusNumber */ 437 438 Status = AcpiOsReadPciConfiguration (PciId, 439 PCI_CFG_PRIMARY_BUS_NUMBER_REG, &PciValue, 8); 440 if (ACPI_FAILURE (Status)) 441 { 442 return (Status); 443 } 444 445 *IsBridge = TRUE; 446 PciId->Bus = (UINT16) PciValue; 447 448 /* Bridge: Get the Secondary BusNumber */ 449 450 Status = AcpiOsReadPciConfiguration (PciId, 451 PCI_CFG_SECONDARY_BUS_NUMBER_REG, &PciValue, 8); 452 if (ACPI_FAILURE (Status)) 453 { 454 return (Status); 455 } 456 457 *BusNumber = (UINT16) PciValue; 458 return (AE_OK); 459 } 460