diff options
Diffstat (limited to 'drivers/pci/hotplug/acpiphp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_pci.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/acpiphp_pci.c b/drivers/pci/hotplug/acpiphp_pci.c new file mode 100644 index 000000000000..54d97c9d1dff --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_pci.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * ACPI PCI HotPlug PCI configuration space management | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001,2002 IBM Corp. | ||
7 | * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | ||
8 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | ||
9 | * Copyright (C) 2002 NEC Corporation | ||
10 | * | ||
11 | * All rights reserved. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or (at | ||
16 | * your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, but | ||
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
21 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
22 | * details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Send feedback to <t-kochi@bq.jp.nec.com> | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/module.h> | ||
34 | |||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include <linux/acpi.h> | ||
38 | #include "../pci.h" | ||
39 | #include "pci_hotplug.h" | ||
40 | #include "acpiphp.h" | ||
41 | |||
42 | #define MY_NAME "acpiphp_pci" | ||
43 | |||
44 | |||
45 | /* allocate mem/pmem/io resource to a new function */ | ||
46 | static int init_config_space (struct acpiphp_func *func) | ||
47 | { | ||
48 | u32 bar, len; | ||
49 | u32 address[] = { | ||
50 | PCI_BASE_ADDRESS_0, | ||
51 | PCI_BASE_ADDRESS_1, | ||
52 | PCI_BASE_ADDRESS_2, | ||
53 | PCI_BASE_ADDRESS_3, | ||
54 | PCI_BASE_ADDRESS_4, | ||
55 | PCI_BASE_ADDRESS_5, | ||
56 | 0 | ||
57 | }; | ||
58 | int count; | ||
59 | struct acpiphp_bridge *bridge; | ||
60 | struct pci_resource *res; | ||
61 | struct pci_bus *pbus; | ||
62 | int bus, device, function; | ||
63 | unsigned int devfn; | ||
64 | u16 tmp; | ||
65 | |||
66 | bridge = func->slot->bridge; | ||
67 | pbus = bridge->pci_bus; | ||
68 | bus = bridge->bus; | ||
69 | device = func->slot->device; | ||
70 | function = func->function; | ||
71 | devfn = PCI_DEVFN(device, function); | ||
72 | |||
73 | for (count = 0; address[count]; count++) { /* for 6 BARs */ | ||
74 | pci_bus_write_config_dword(pbus, devfn, | ||
75 | address[count], 0xFFFFFFFF); | ||
76 | pci_bus_read_config_dword(pbus, devfn, address[count], &bar); | ||
77 | |||
78 | if (!bar) /* This BAR is not implemented */ | ||
79 | continue; | ||
80 | |||
81 | dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar); | ||
82 | |||
83 | if (bar & PCI_BASE_ADDRESS_SPACE_IO) { | ||
84 | /* This is IO */ | ||
85 | |||
86 | len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); | ||
87 | len = len & ~(len - 1); | ||
88 | |||
89 | dbg("len in IO %x, BAR %d\n", len, count); | ||
90 | |||
91 | spin_lock(&bridge->res_lock); | ||
92 | res = acpiphp_get_io_resource(&bridge->io_head, len); | ||
93 | spin_unlock(&bridge->res_lock); | ||
94 | |||
95 | if (!res) { | ||
96 | err("cannot allocate requested io for %02x:%02x.%d len %x\n", | ||
97 | bus, device, function, len); | ||
98 | return -1; | ||
99 | } | ||
100 | pci_bus_write_config_dword(pbus, devfn, | ||
101 | address[count], | ||
102 | (u32)res->base); | ||
103 | res->next = func->io_head; | ||
104 | func->io_head = res; | ||
105 | |||
106 | } else { | ||
107 | /* This is Memory */ | ||
108 | if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
109 | /* pfmem */ | ||
110 | |||
111 | len = bar & 0xFFFFFFF0; | ||
112 | len = ~len + 1; | ||
113 | |||
114 | dbg("len in PFMEM %x, BAR %d\n", len, count); | ||
115 | |||
116 | spin_lock(&bridge->res_lock); | ||
117 | res = acpiphp_get_resource(&bridge->p_mem_head, len); | ||
118 | spin_unlock(&bridge->res_lock); | ||
119 | |||
120 | if (!res) { | ||
121 | err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", | ||
122 | bus, device, function, len); | ||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | pci_bus_write_config_dword(pbus, devfn, | ||
127 | address[count], | ||
128 | (u32)res->base); | ||
129 | |||
130 | if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ | ||
131 | dbg("inside the pfmem 64 case, count %d\n", count); | ||
132 | count += 1; | ||
133 | pci_bus_write_config_dword(pbus, devfn, | ||
134 | address[count], | ||
135 | (u32)(res->base >> 32)); | ||
136 | } | ||
137 | |||
138 | res->next = func->p_mem_head; | ||
139 | func->p_mem_head = res; | ||
140 | |||
141 | } else { | ||
142 | /* regular memory */ | ||
143 | |||
144 | len = bar & 0xFFFFFFF0; | ||
145 | len = ~len + 1; | ||
146 | |||
147 | dbg("len in MEM %x, BAR %d\n", len, count); | ||
148 | |||
149 | spin_lock(&bridge->res_lock); | ||
150 | res = acpiphp_get_resource(&bridge->mem_head, len); | ||
151 | spin_unlock(&bridge->res_lock); | ||
152 | |||
153 | if (!res) { | ||
154 | err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", | ||
155 | bus, device, function, len); | ||
156 | return -1; | ||
157 | } | ||
158 | |||
159 | pci_bus_write_config_dword(pbus, devfn, | ||
160 | address[count], | ||
161 | (u32)res->base); | ||
162 | |||
163 | if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
164 | /* takes up another dword */ | ||
165 | dbg("inside mem 64 case, reg. mem, count %d\n", count); | ||
166 | count += 1; | ||
167 | pci_bus_write_config_dword(pbus, devfn, | ||
168 | address[count], | ||
169 | (u32)(res->base >> 32)); | ||
170 | } | ||
171 | |||
172 | res->next = func->mem_head; | ||
173 | func->mem_head = res; | ||
174 | |||
175 | } | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* disable expansion rom */ | ||
180 | pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000); | ||
181 | |||
182 | /* set PCI parameters from _HPP */ | ||
183 | pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE, | ||
184 | bridge->hpp.cache_line_size); | ||
185 | pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER, | ||
186 | bridge->hpp.latency_timer); | ||
187 | |||
188 | pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp); | ||
189 | if (bridge->hpp.enable_SERR) | ||
190 | tmp |= PCI_COMMAND_SERR; | ||
191 | if (bridge->hpp.enable_PERR) | ||
192 | tmp |= PCI_COMMAND_PARITY; | ||
193 | pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | /* detect_used_resource - subtract resource under dev from bridge */ | ||
199 | static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev) | ||
200 | { | ||
201 | int count; | ||
202 | |||
203 | dbg("Device %s\n", pci_name(dev)); | ||
204 | |||
205 | for (count = 0; count < DEVICE_COUNT_RESOURCE; count++) { | ||
206 | struct pci_resource *res; | ||
207 | struct pci_resource **head; | ||
208 | unsigned long base = dev->resource[count].start; | ||
209 | unsigned long len = dev->resource[count].end - base + 1; | ||
210 | unsigned long flags = dev->resource[count].flags; | ||
211 | |||
212 | if (!flags) | ||
213 | continue; | ||
214 | |||
215 | dbg("BAR[%d] 0x%lx - 0x%lx (0x%lx)\n", count, base, | ||
216 | base + len - 1, flags); | ||
217 | |||
218 | if (flags & IORESOURCE_IO) { | ||
219 | head = &bridge->io_head; | ||
220 | } else if (flags & IORESOURCE_PREFETCH) { | ||
221 | head = &bridge->p_mem_head; | ||
222 | } else { | ||
223 | head = &bridge->mem_head; | ||
224 | } | ||
225 | |||
226 | spin_lock(&bridge->res_lock); | ||
227 | res = acpiphp_get_resource_with_base(head, base, len); | ||
228 | spin_unlock(&bridge->res_lock); | ||
229 | if (res) | ||
230 | kfree(res); | ||
231 | } | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | |||
237 | /** | ||
238 | * acpiphp_detect_pci_resource - detect resources under bridge | ||
239 | * @bridge: detect all resources already used under this bridge | ||
240 | * | ||
241 | * collect all resources already allocated for all devices under a bridge. | ||
242 | */ | ||
243 | int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge) | ||
244 | { | ||
245 | struct list_head *l; | ||
246 | struct pci_dev *dev; | ||
247 | |||
248 | list_for_each (l, &bridge->pci_bus->devices) { | ||
249 | dev = pci_dev_b(l); | ||
250 | detect_used_resource(bridge, dev); | ||
251 | } | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | |||
257 | /** | ||
258 | * acpiphp_init_slot_resource - gather resource usage information of a slot | ||
259 | * @slot: ACPI slot object to be checked, should have valid pci_dev member | ||
260 | * | ||
261 | * TBD: PCI-to-PCI bridge case | ||
262 | * use pci_dev->resource[] | ||
263 | */ | ||
264 | int acpiphp_init_func_resource (struct acpiphp_func *func) | ||
265 | { | ||
266 | u64 base; | ||
267 | u32 bar, len; | ||
268 | u32 address[] = { | ||
269 | PCI_BASE_ADDRESS_0, | ||
270 | PCI_BASE_ADDRESS_1, | ||
271 | PCI_BASE_ADDRESS_2, | ||
272 | PCI_BASE_ADDRESS_3, | ||
273 | PCI_BASE_ADDRESS_4, | ||
274 | PCI_BASE_ADDRESS_5, | ||
275 | 0 | ||
276 | }; | ||
277 | int count; | ||
278 | struct pci_resource *res; | ||
279 | struct pci_dev *dev; | ||
280 | |||
281 | dev = func->pci_dev; | ||
282 | dbg("Hot-pluggable device %s\n", pci_name(dev)); | ||
283 | |||
284 | for (count = 0; address[count]; count++) { /* for 6 BARs */ | ||
285 | pci_read_config_dword(dev, address[count], &bar); | ||
286 | |||
287 | if (!bar) /* This BAR is not implemented */ | ||
288 | continue; | ||
289 | |||
290 | pci_write_config_dword(dev, address[count], 0xFFFFFFFF); | ||
291 | pci_read_config_dword(dev, address[count], &len); | ||
292 | |||
293 | if (len & PCI_BASE_ADDRESS_SPACE_IO) { | ||
294 | /* This is IO */ | ||
295 | base = bar & 0xFFFFFFFC; | ||
296 | len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); | ||
297 | len = len & ~(len - 1); | ||
298 | |||
299 | dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); | ||
300 | |||
301 | res = acpiphp_make_resource(base, len); | ||
302 | if (!res) | ||
303 | goto no_memory; | ||
304 | |||
305 | res->next = func->io_head; | ||
306 | func->io_head = res; | ||
307 | |||
308 | } else { | ||
309 | /* This is Memory */ | ||
310 | base = bar & 0xFFFFFFF0; | ||
311 | if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
312 | /* pfmem */ | ||
313 | |||
314 | len &= 0xFFFFFFF0; | ||
315 | len = ~len + 1; | ||
316 | |||
317 | if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ | ||
318 | dbg("prefetch mem 64\n"); | ||
319 | count += 1; | ||
320 | } | ||
321 | dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1); | ||
322 | res = acpiphp_make_resource(base, len); | ||
323 | if (!res) | ||
324 | goto no_memory; | ||
325 | |||
326 | res->next = func->p_mem_head; | ||
327 | func->p_mem_head = res; | ||
328 | |||
329 | } else { | ||
330 | /* regular memory */ | ||
331 | |||
332 | len &= 0xFFFFFFF0; | ||
333 | len = ~len + 1; | ||
334 | |||
335 | if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
336 | /* takes up another dword */ | ||
337 | dbg("mem 64\n"); | ||
338 | count += 1; | ||
339 | } | ||
340 | dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1); | ||
341 | res = acpiphp_make_resource(base, len); | ||
342 | if (!res) | ||
343 | goto no_memory; | ||
344 | |||
345 | res->next = func->mem_head; | ||
346 | func->mem_head = res; | ||
347 | |||
348 | } | ||
349 | } | ||
350 | |||
351 | pci_write_config_dword(dev, address[count], bar); | ||
352 | } | ||
353 | #if 1 | ||
354 | acpiphp_dump_func_resource(func); | ||
355 | #endif | ||
356 | |||
357 | return 0; | ||
358 | |||
359 | no_memory: | ||
360 | err("out of memory\n"); | ||
361 | acpiphp_free_resource(&func->io_head); | ||
362 | acpiphp_free_resource(&func->mem_head); | ||
363 | acpiphp_free_resource(&func->p_mem_head); | ||
364 | |||
365 | return -1; | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * acpiphp_configure_slot - allocate PCI resources | ||
371 | * @slot: slot to be configured | ||
372 | * | ||
373 | * initializes a PCI functions on a device inserted | ||
374 | * into the slot | ||
375 | * | ||
376 | */ | ||
377 | int acpiphp_configure_slot (struct acpiphp_slot *slot) | ||
378 | { | ||
379 | struct acpiphp_func *func; | ||
380 | struct list_head *l; | ||
381 | u8 hdr; | ||
382 | u32 dvid; | ||
383 | int retval = 0; | ||
384 | int is_multi = 0; | ||
385 | |||
386 | pci_bus_read_config_byte(slot->bridge->pci_bus, | ||
387 | PCI_DEVFN(slot->device, 0), | ||
388 | PCI_HEADER_TYPE, &hdr); | ||
389 | |||
390 | if (hdr & 0x80) | ||
391 | is_multi = 1; | ||
392 | |||
393 | list_for_each (l, &slot->funcs) { | ||
394 | func = list_entry(l, struct acpiphp_func, sibling); | ||
395 | if (is_multi || func->function == 0) { | ||
396 | pci_bus_read_config_dword(slot->bridge->pci_bus, | ||
397 | PCI_DEVFN(slot->device, | ||
398 | func->function), | ||
399 | PCI_VENDOR_ID, &dvid); | ||
400 | if (dvid != 0xffffffff) { | ||
401 | retval = init_config_space(func); | ||
402 | if (retval) | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | return retval; | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * acpiphp_configure_function - configure PCI function | ||
413 | * @func: function to be configured | ||
414 | * | ||
415 | * initializes a PCI functions on a device inserted | ||
416 | * into the slot | ||
417 | * | ||
418 | */ | ||
419 | int acpiphp_configure_function (struct acpiphp_func *func) | ||
420 | { | ||
421 | /* all handled by the pci core now */ | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /** | ||
426 | * acpiphp_unconfigure_function - unconfigure PCI function | ||
427 | * @func: function to be unconfigured | ||
428 | * | ||
429 | */ | ||
430 | void acpiphp_unconfigure_function (struct acpiphp_func *func) | ||
431 | { | ||
432 | struct acpiphp_bridge *bridge; | ||
433 | |||
434 | /* if pci_dev is NULL, ignore it */ | ||
435 | if (!func->pci_dev) | ||
436 | return; | ||
437 | |||
438 | pci_remove_bus_device(func->pci_dev); | ||
439 | |||
440 | /* free all resources */ | ||
441 | bridge = func->slot->bridge; | ||
442 | |||
443 | spin_lock(&bridge->res_lock); | ||
444 | acpiphp_move_resource(&func->io_head, &bridge->io_head); | ||
445 | acpiphp_move_resource(&func->mem_head, &bridge->mem_head); | ||
446 | acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head); | ||
447 | acpiphp_move_resource(&func->bus_head, &bridge->bus_head); | ||
448 | spin_unlock(&bridge->res_lock); | ||
449 | } | ||