• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
acpi_hw_derive_pci_id(struct acpi_pci_id * pci_id,acpi_handle root_pci_device,acpi_handle pci_region)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, &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
acpi_hw_build_pci_list(acpi_handle root_pci_device,acpi_handle pci_region,struct acpi_pci_device ** return_list_head)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(current_device, &parent_device);
154 		if (ACPI_FAILURE(status)) {
155 
156 			/* Must delete the list before exit */
157 
158 			acpi_hw_delete_pci_list(*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(*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
acpi_hw_process_pci_list(struct acpi_pci_id * pci_id,struct acpi_pci_device * list_head)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, info->device,
232 						     &bus_number, &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 
acpi_hw_delete_pci_list(struct acpi_pci_device * list_head)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
acpi_hw_get_pci_device_info(struct acpi_pci_id * pci_id,acpi_handle pci_device,u16 * bus_number,u8 * is_bridge)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(pci_device, &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 						 pci_device, &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 						&pci_value, 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 						&pci_value, 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 						&pci_value, 8);
378 	if (ACPI_FAILURE(status)) {
379 		return (status);
380 	}
381 
382 	*bus_number = (u16)pci_value;
383 	return (AE_OK);
384 }
385