Branch data Line data Source code
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 PCI_CFG_HEADER_TYPE_REG 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 PCI_HEADER_TYPE_MASK 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 : 42 : acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
87 : : acpi_handle root_pci_device, acpi_handle pci_region)
88 : : {
89 : 42 : acpi_status status;
90 : 42 : struct acpi_pci_device *list_head;
91 : :
92 : 42 : ACPI_FUNCTION_TRACE(hw_derive_pci_id);
93 : :
94 [ + - ]: 42 : 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 : 42 : status =
101 : 42 : acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);
102 [ + - ]: 42 : if (ACPI_SUCCESS(status)) {
103 : :
104 : : /* Walk the list, updating the PCI device/function/bus numbers */
105 : :
106 : 42 : status = acpi_hw_process_pci_list(pci_id, list_head);
107 : :
108 : : /* Delete the list */
109 : :
110 : 42 : 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 : 42 : 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 : 42 : acpi_handle current_device;
141 : 42 : acpi_handle parent_device;
142 : 42 : acpi_status status;
143 : 42 : 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 : 42 : *return_list_head = NULL;
151 : 42 : current_device = pci_region;
152 : 126 : while (1) {
153 : 84 : status = acpi_get_parent(current_device, &parent_device);
154 [ - + ]: 84 : if (ACPI_FAILURE(status)) {
155 : :
156 : : /* Must delete the list before exit */
157 : :
158 : 0 : acpi_hw_delete_pci_list(*return_list_head);
159 : 0 : return (status);
160 : : }
161 : :
162 : : /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
163 : :
164 [ + + ]: 84 : if (parent_device == root_pci_device) {
165 : : return (AE_OK);
166 : : }
167 : :
168 : 42 : list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
169 [ - + ]: 42 : if (!list_element) {
170 : :
171 : : /* Must delete the list before exit */
172 : :
173 : 0 : acpi_hw_delete_pci_list(*return_list_head);
174 : 0 : return (AE_NO_MEMORY);
175 : : }
176 : :
177 : : /* Put new element at the head of the list */
178 : :
179 : 42 : list_element->next = *return_list_head;
180 : 42 : list_element->device = parent_device;
181 : 42 : *return_list_head = list_element;
182 : :
183 : 42 : 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 : 42 : acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
206 : : struct acpi_pci_device *list_head)
207 : : {
208 : 42 : acpi_status status = AE_OK;
209 : 42 : struct acpi_pci_device *info;
210 : 42 : u16 bus_number;
211 : 42 : u8 is_bridge = TRUE;
212 : :
213 : 42 : 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 : 42 : pci_id->function));
219 : :
220 : 42 : 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 : 42 : info = list_head;
230 [ + + ]: 84 : while (info) {
231 : 42 : status = acpi_hw_get_pci_device_info(pci_id, info->device,
232 : : &bus_number, &is_bridge);
233 [ - + ]: 42 : if (ACPI_FAILURE(status)) {
234 : 0 : return (status);
235 : : }
236 : :
237 : 42 : 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 : 42 : static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)
263 : : {
264 : 42 : struct acpi_pci_device *next;
265 : 42 : struct acpi_pci_device *previous;
266 : :
267 : 42 : next = list_head;
268 [ + + ]: 84 : while (next) {
269 : 42 : previous = next;
270 : 42 : next = previous->next;
271 : 42 : ACPI_FREE(previous);
272 : : }
273 : 42 : }
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 : 42 : 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 : 42 : acpi_status status;
301 : 42 : acpi_object_type object_type;
302 : 42 : u64 return_value;
303 : 42 : u64 pci_value;
304 : :
305 : : /* We only care about objects of type Device */
306 : :
307 : 42 : status = acpi_get_type(pci_device, &object_type);
308 [ + - ]: 42 : if (ACPI_FAILURE(status)) {
309 : : return (status);
310 : : }
311 : :
312 [ + - ]: 42 : if (object_type != ACPI_TYPE_DEVICE) {
313 : : return (AE_OK);
314 : : }
315 : :
316 : : /* We need an _ADR. Ignore device if not present */
317 : :
318 : 42 : status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
319 : : pci_device, &return_value);
320 [ + - ]: 42 : 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 : 42 : pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));
329 : 42 : 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 [ + - ]: 42 : if (*is_bridge) {
336 : 42 : 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 : 42 : *is_bridge = FALSE;
345 : 42 : status = acpi_os_read_pci_configuration(pci_id,
346 : : PCI_CFG_HEADER_TYPE_REG,
347 : : &pci_value, 8);
348 [ + - ]: 42 : if (ACPI_FAILURE(status)) {
349 : : return (status);
350 : : }
351 : :
352 : : /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */
353 : :
354 : 42 : pci_value &= PCI_HEADER_TYPE_MASK;
355 : :
356 [ - + ]: 42 : 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 : 0 : status = acpi_os_read_pci_configuration(pci_id,
364 : : PCI_CFG_PRIMARY_BUS_NUMBER_REG,
365 : : &pci_value, 8);
366 [ # # ]: 0 : if (ACPI_FAILURE(status)) {
367 : : return (status);
368 : : }
369 : :
370 : 0 : *is_bridge = TRUE;
371 : 0 : pci_id->bus = (u16)pci_value;
372 : :
373 : : /* Bridge: Get the Secondary bus_number */
374 : :
375 : 0 : status = acpi_os_read_pci_configuration(pci_id,
376 : : PCI_CFG_SECONDARY_BUS_NUMBER_REG,
377 : : &pci_value, 8);
378 [ # # ]: 0 : if (ACPI_FAILURE(status)) {
379 : : return (status);
380 : : }
381 : :
382 : 0 : *bus_number = (u16)pci_value;
383 : 0 : return (AE_OK);
384 : : }
|