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