diff options
Diffstat (limited to 'drivers/acpi/pci_root.c')
-rw-r--r-- | drivers/acpi/pci_root.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c new file mode 100644 index 000000000000..7e6b8e3b2ed4 --- /dev/null +++ b/drivers/acpi/pci_root.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 40 $) | ||
3 | * | ||
4 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> | ||
5 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | ||
6 | * | ||
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
22 | * | ||
23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/proc_fs.h> | ||
31 | #include <linux/spinlock.h> | ||
32 | #include <linux/pm.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/acpi.h> | ||
35 | #include <acpi/acpi_bus.h> | ||
36 | #include <acpi/acpi_drivers.h> | ||
37 | |||
38 | |||
39 | #define _COMPONENT ACPI_PCI_COMPONENT | ||
40 | ACPI_MODULE_NAME ("pci_root") | ||
41 | |||
42 | #define ACPI_PCI_ROOT_CLASS "pci_bridge" | ||
43 | #define ACPI_PCI_ROOT_HID "PNP0A03" | ||
44 | #define ACPI_PCI_ROOT_DRIVER_NAME "ACPI PCI Root Bridge Driver" | ||
45 | #define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge" | ||
46 | |||
47 | static int acpi_pci_root_add (struct acpi_device *device); | ||
48 | static int acpi_pci_root_remove (struct acpi_device *device, int type); | ||
49 | |||
50 | static struct acpi_driver acpi_pci_root_driver = { | ||
51 | .name = ACPI_PCI_ROOT_DRIVER_NAME, | ||
52 | .class = ACPI_PCI_ROOT_CLASS, | ||
53 | .ids = ACPI_PCI_ROOT_HID, | ||
54 | .ops = { | ||
55 | .add = acpi_pci_root_add, | ||
56 | .remove = acpi_pci_root_remove, | ||
57 | }, | ||
58 | }; | ||
59 | |||
60 | struct acpi_pci_root { | ||
61 | struct list_head node; | ||
62 | acpi_handle handle; | ||
63 | struct acpi_pci_id id; | ||
64 | struct pci_bus *bus; | ||
65 | }; | ||
66 | |||
67 | static LIST_HEAD(acpi_pci_roots); | ||
68 | |||
69 | static struct acpi_pci_driver *sub_driver; | ||
70 | |||
71 | int acpi_pci_register_driver(struct acpi_pci_driver *driver) | ||
72 | { | ||
73 | int n = 0; | ||
74 | struct list_head *entry; | ||
75 | |||
76 | struct acpi_pci_driver **pptr = &sub_driver; | ||
77 | while (*pptr) | ||
78 | pptr = &(*pptr)->next; | ||
79 | *pptr = driver; | ||
80 | |||
81 | if (!driver->add) | ||
82 | return 0; | ||
83 | |||
84 | list_for_each(entry, &acpi_pci_roots) { | ||
85 | struct acpi_pci_root *root; | ||
86 | root = list_entry(entry, struct acpi_pci_root, node); | ||
87 | driver->add(root->handle); | ||
88 | n++; | ||
89 | } | ||
90 | |||
91 | return n; | ||
92 | } | ||
93 | EXPORT_SYMBOL(acpi_pci_register_driver); | ||
94 | |||
95 | void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) | ||
96 | { | ||
97 | struct list_head *entry; | ||
98 | |||
99 | struct acpi_pci_driver **pptr = &sub_driver; | ||
100 | while (*pptr) { | ||
101 | if (*pptr != driver) | ||
102 | continue; | ||
103 | *pptr = (*pptr)->next; | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | if (!driver->remove) | ||
108 | return; | ||
109 | |||
110 | list_for_each(entry, &acpi_pci_roots) { | ||
111 | struct acpi_pci_root *root; | ||
112 | root = list_entry(entry, struct acpi_pci_root, node); | ||
113 | driver->remove(root->handle); | ||
114 | } | ||
115 | } | ||
116 | EXPORT_SYMBOL(acpi_pci_unregister_driver); | ||
117 | |||
118 | static acpi_status | ||
119 | get_root_bridge_busnr_callback (struct acpi_resource *resource, void *data) | ||
120 | { | ||
121 | int *busnr = (int *)data; | ||
122 | struct acpi_resource_address64 address; | ||
123 | |||
124 | if (resource->id != ACPI_RSTYPE_ADDRESS16 && | ||
125 | resource->id != ACPI_RSTYPE_ADDRESS32 && | ||
126 | resource->id != ACPI_RSTYPE_ADDRESS64) | ||
127 | return AE_OK; | ||
128 | |||
129 | acpi_resource_to_address64(resource, &address); | ||
130 | if ((address.address_length > 0) && | ||
131 | (address.resource_type == ACPI_BUS_NUMBER_RANGE)) | ||
132 | *busnr = address.min_address_range; | ||
133 | |||
134 | return AE_OK; | ||
135 | } | ||
136 | |||
137 | static acpi_status | ||
138 | try_get_root_bridge_busnr(acpi_handle handle, int *busnum) | ||
139 | { | ||
140 | acpi_status status; | ||
141 | |||
142 | *busnum = -1; | ||
143 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, get_root_bridge_busnr_callback, busnum); | ||
144 | if (ACPI_FAILURE(status)) | ||
145 | return status; | ||
146 | /* Check if we really get a bus number from _CRS */ | ||
147 | if (*busnum == -1) | ||
148 | return AE_ERROR; | ||
149 | return AE_OK; | ||
150 | } | ||
151 | |||
152 | static int | ||
153 | acpi_pci_root_add ( | ||
154 | struct acpi_device *device) | ||
155 | { | ||
156 | int result = 0; | ||
157 | struct acpi_pci_root *root = NULL; | ||
158 | struct acpi_pci_root *tmp; | ||
159 | acpi_status status = AE_OK; | ||
160 | unsigned long value = 0; | ||
161 | acpi_handle handle = NULL; | ||
162 | |||
163 | ACPI_FUNCTION_TRACE("acpi_pci_root_add"); | ||
164 | |||
165 | if (!device) | ||
166 | return_VALUE(-EINVAL); | ||
167 | |||
168 | root = kmalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); | ||
169 | if (!root) | ||
170 | return_VALUE(-ENOMEM); | ||
171 | memset(root, 0, sizeof(struct acpi_pci_root)); | ||
172 | |||
173 | root->handle = device->handle; | ||
174 | strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME); | ||
175 | strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); | ||
176 | acpi_driver_data(device) = root; | ||
177 | |||
178 | /* | ||
179 | * TBD: Doesn't the bus driver automatically set this? | ||
180 | */ | ||
181 | device->ops.bind = acpi_pci_bind; | ||
182 | |||
183 | /* | ||
184 | * Segment | ||
185 | * ------- | ||
186 | * Obtained via _SEG, if exists, otherwise assumed to be zero (0). | ||
187 | */ | ||
188 | status = acpi_evaluate_integer(root->handle, METHOD_NAME__SEG, NULL, | ||
189 | &value); | ||
190 | switch (status) { | ||
191 | case AE_OK: | ||
192 | root->id.segment = (u16) value; | ||
193 | break; | ||
194 | case AE_NOT_FOUND: | ||
195 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
196 | "Assuming segment 0 (no _SEG)\n")); | ||
197 | root->id.segment = 0; | ||
198 | break; | ||
199 | default: | ||
200 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SEG\n")); | ||
201 | result = -ENODEV; | ||
202 | goto end; | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Bus | ||
207 | * --- | ||
208 | * Obtained via _BBN, if exists, otherwise assumed to be zero (0). | ||
209 | */ | ||
210 | status = acpi_evaluate_integer(root->handle, METHOD_NAME__BBN, NULL, | ||
211 | &value); | ||
212 | switch (status) { | ||
213 | case AE_OK: | ||
214 | root->id.bus = (u16) value; | ||
215 | break; | ||
216 | case AE_NOT_FOUND: | ||
217 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n")); | ||
218 | root->id.bus = 0; | ||
219 | break; | ||
220 | default: | ||
221 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BBN\n")); | ||
222 | result = -ENODEV; | ||
223 | goto end; | ||
224 | } | ||
225 | |||
226 | /* Some systems have wrong _BBN */ | ||
227 | list_for_each_entry(tmp, &acpi_pci_roots, node) { | ||
228 | if ((tmp->id.segment == root->id.segment) | ||
229 | && (tmp->id.bus == root->id.bus)) { | ||
230 | int bus = 0; | ||
231 | acpi_status status; | ||
232 | |||
233 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
234 | "Wrong _BBN value, please reboot and using option 'pci=noacpi'\n")); | ||
235 | |||
236 | status = try_get_root_bridge_busnr(root->handle, &bus); | ||
237 | if (ACPI_FAILURE(status)) | ||
238 | break; | ||
239 | if (bus != root->id.bus) { | ||
240 | printk(KERN_INFO PREFIX "PCI _CRS %d overrides _BBN 0\n", bus); | ||
241 | root->id.bus = bus; | ||
242 | } | ||
243 | break; | ||
244 | } | ||
245 | } | ||
246 | /* | ||
247 | * Device & Function | ||
248 | * ----------------- | ||
249 | * Obtained from _ADR (which has already been evaluated for us). | ||
250 | */ | ||
251 | root->id.device = device->pnp.bus_address >> 16; | ||
252 | root->id.function = device->pnp.bus_address & 0xFFFF; | ||
253 | |||
254 | /* | ||
255 | * TBD: Need PCI interface for enumeration/configuration of roots. | ||
256 | */ | ||
257 | |||
258 | /* TBD: Locking */ | ||
259 | list_add_tail(&root->node, &acpi_pci_roots); | ||
260 | |||
261 | printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n", | ||
262 | acpi_device_name(device), acpi_device_bid(device), | ||
263 | root->id.segment, root->id.bus); | ||
264 | |||
265 | /* | ||
266 | * Scan the Root Bridge | ||
267 | * -------------------- | ||
268 | * Must do this prior to any attempt to bind the root device, as the | ||
269 | * PCI namespace does not get created until this call is made (and | ||
270 | * thus the root bridge's pci_dev does not exist). | ||
271 | */ | ||
272 | root->bus = pci_acpi_scan_root(device, root->id.segment, root->id.bus); | ||
273 | if (!root->bus) { | ||
274 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
275 | "Bus %04x:%02x not present in PCI namespace\n", | ||
276 | root->id.segment, root->id.bus)); | ||
277 | result = -ENODEV; | ||
278 | goto end; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * Attach ACPI-PCI Context | ||
283 | * ----------------------- | ||
284 | * Thus binding the ACPI and PCI devices. | ||
285 | */ | ||
286 | result = acpi_pci_bind_root(device, &root->id, root->bus); | ||
287 | if (result) | ||
288 | goto end; | ||
289 | |||
290 | /* | ||
291 | * PCI Routing Table | ||
292 | * ----------------- | ||
293 | * Evaluate and parse _PRT, if exists. | ||
294 | */ | ||
295 | status = acpi_get_handle(root->handle, METHOD_NAME__PRT, &handle); | ||
296 | if (ACPI_SUCCESS(status)) | ||
297 | result = acpi_pci_irq_add_prt(root->handle, root->id.segment, | ||
298 | root->id.bus); | ||
299 | |||
300 | end: | ||
301 | if (result) | ||
302 | kfree(root); | ||
303 | |||
304 | return_VALUE(result); | ||
305 | } | ||
306 | |||
307 | |||
308 | static int | ||
309 | acpi_pci_root_remove ( | ||
310 | struct acpi_device *device, | ||
311 | int type) | ||
312 | { | ||
313 | struct acpi_pci_root *root = NULL; | ||
314 | |||
315 | ACPI_FUNCTION_TRACE("acpi_pci_root_remove"); | ||
316 | |||
317 | if (!device || !acpi_driver_data(device)) | ||
318 | return_VALUE(-EINVAL); | ||
319 | |||
320 | root = (struct acpi_pci_root *) acpi_driver_data(device); | ||
321 | |||
322 | kfree(root); | ||
323 | |||
324 | return_VALUE(0); | ||
325 | } | ||
326 | |||
327 | |||
328 | static int __init acpi_pci_root_init (void) | ||
329 | { | ||
330 | ACPI_FUNCTION_TRACE("acpi_pci_root_init"); | ||
331 | |||
332 | if (acpi_pci_disabled) | ||
333 | return_VALUE(0); | ||
334 | |||
335 | /* DEBUG: | ||
336 | acpi_dbg_layer = ACPI_PCI_COMPONENT; | ||
337 | acpi_dbg_level = 0xFFFFFFFF; | ||
338 | */ | ||
339 | |||
340 | if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0) | ||
341 | return_VALUE(-ENODEV); | ||
342 | |||
343 | return_VALUE(0); | ||
344 | } | ||
345 | |||
346 | subsys_initcall(acpi_pci_root_init); | ||
347 | |||