Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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