diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/shpchp_pci.c | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c new file mode 100644 index 000000000000..90113e9cd69b --- /dev/null +++ b/drivers/pci/hotplug/shpchp_pci.c | |||
@@ -0,0 +1,810 @@ | |||
1 | /* | ||
2 | * Standard Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <linux/proc_fs.h> | ||
37 | #include <linux/pci.h> | ||
38 | #include "../pci.h" | ||
39 | #include "shpchp.h" | ||
40 | #ifndef CONFIG_IA64 | ||
41 | #include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */ | ||
42 | #endif | ||
43 | |||
44 | int shpchp_configure_device (struct controller* ctrl, struct pci_func* func) | ||
45 | { | ||
46 | unsigned char bus; | ||
47 | struct pci_bus *child; | ||
48 | int num; | ||
49 | |||
50 | if (func->pci_dev == NULL) | ||
51 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
52 | |||
53 | /* Still NULL ? Well then scan for it ! */ | ||
54 | if (func->pci_dev == NULL) { | ||
55 | num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function)); | ||
56 | if (num) { | ||
57 | dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate, | ||
58 | ctrl->pci_dev->subordinate->number); | ||
59 | pci_bus_add_devices(ctrl->pci_dev->subordinate); | ||
60 | } | ||
61 | |||
62 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
63 | if (func->pci_dev == NULL) { | ||
64 | dbg("ERROR: pci_dev still null\n"); | ||
65 | return 0; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
70 | pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); | ||
71 | child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); | ||
72 | pci_do_scan_bus(child); | ||
73 | |||
74 | } | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | |||
80 | int shpchp_unconfigure_device(struct pci_func* func) | ||
81 | { | ||
82 | int rc = 0; | ||
83 | int j; | ||
84 | |||
85 | dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, | ||
86 | func->device, func->function); | ||
87 | |||
88 | for (j=0; j<8 ; j++) { | ||
89 | struct pci_dev* temp = pci_find_slot(func->bus, | ||
90 | (func->device << 3) | j); | ||
91 | if (temp) { | ||
92 | pci_remove_bus_device(temp); | ||
93 | } | ||
94 | } | ||
95 | return rc; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * shpchp_set_irq | ||
100 | * | ||
101 | * @bus_num: bus number of PCI device | ||
102 | * @dev_num: device number of PCI device | ||
103 | * @slot: pointer to u8 where slot number will be returned | ||
104 | */ | ||
105 | int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) | ||
106 | { | ||
107 | #if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64) | ||
108 | int rc; | ||
109 | u16 temp_word; | ||
110 | struct pci_dev fakedev; | ||
111 | struct pci_bus fakebus; | ||
112 | |||
113 | fakedev.devfn = dev_num << 3; | ||
114 | fakedev.bus = &fakebus; | ||
115 | fakebus.number = bus_num; | ||
116 | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | ||
117 | __FUNCTION__, dev_num, bus_num, int_pin, irq_num); | ||
118 | rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); | ||
119 | dbg("%s: rc %d\n", __FUNCTION__, rc); | ||
120 | if (!rc) | ||
121 | return !rc; | ||
122 | |||
123 | /* set the Edge Level Control Register (ELCR) */ | ||
124 | temp_word = inb(0x4d0); | ||
125 | temp_word |= inb(0x4d1) << 8; | ||
126 | |||
127 | temp_word |= 0x01 << irq_num; | ||
128 | |||
129 | /* This should only be for x86 as it sets the Edge Level Control Register */ | ||
130 | outb((u8) (temp_word & 0xFF), 0x4d0); | ||
131 | outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); | ||
132 | #endif | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* More PCI configuration routines; this time centered around hotplug controller */ | ||
137 | |||
138 | |||
139 | /* | ||
140 | * shpchp_save_config | ||
141 | * | ||
142 | * Reads configuration for all slots in a PCI bus and saves info. | ||
143 | * | ||
144 | * Note: For non-hot plug busses, the slot # saved is the device # | ||
145 | * | ||
146 | * returns 0 if success | ||
147 | */ | ||
148 | int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num) | ||
149 | { | ||
150 | int rc; | ||
151 | u8 class_code; | ||
152 | u8 header_type; | ||
153 | u32 ID; | ||
154 | u8 secondary_bus; | ||
155 | struct pci_func *new_slot; | ||
156 | int sub_bus; | ||
157 | int FirstSupported; | ||
158 | int LastSupported; | ||
159 | int max_functions; | ||
160 | int function; | ||
161 | u8 DevError; | ||
162 | int device = 0; | ||
163 | int cloop = 0; | ||
164 | int stop_it; | ||
165 | int index; | ||
166 | int is_hot_plug = num_ctlr_slots || first_device_num; | ||
167 | struct pci_bus lpci_bus, *pci_bus; | ||
168 | |||
169 | dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, | ||
170 | num_ctlr_slots, first_device_num); | ||
171 | |||
172 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
173 | pci_bus = &lpci_bus; | ||
174 | |||
175 | dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, | ||
176 | num_ctlr_slots, first_device_num); | ||
177 | |||
178 | /* Decide which slots are supported */ | ||
179 | if (is_hot_plug) { | ||
180 | /********************************* | ||
181 | * is_hot_plug is the slot mask | ||
182 | *********************************/ | ||
183 | FirstSupported = first_device_num; | ||
184 | LastSupported = FirstSupported + num_ctlr_slots - 1; | ||
185 | } else { | ||
186 | FirstSupported = 0; | ||
187 | LastSupported = 0x1F; | ||
188 | } | ||
189 | |||
190 | dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, | ||
191 | LastSupported); | ||
192 | |||
193 | /* Save PCI configuration space for all devices in supported slots */ | ||
194 | pci_bus->number = busnumber; | ||
195 | for (device = FirstSupported; device <= LastSupported; device++) { | ||
196 | ID = 0xFFFFFFFF; | ||
197 | rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), | ||
198 | PCI_VENDOR_ID, &ID); | ||
199 | |||
200 | if (ID != 0xFFFFFFFF) { /* device in slot */ | ||
201 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | ||
202 | 0x0B, &class_code); | ||
203 | if (rc) | ||
204 | return rc; | ||
205 | |||
206 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | ||
207 | PCI_HEADER_TYPE, &header_type); | ||
208 | if (rc) | ||
209 | return rc; | ||
210 | |||
211 | dbg("class_code = %x, header_type = %x\n", class_code, header_type); | ||
212 | |||
213 | /* If multi-function device, set max_functions to 8 */ | ||
214 | if (header_type & 0x80) | ||
215 | max_functions = 8; | ||
216 | else | ||
217 | max_functions = 1; | ||
218 | |||
219 | function = 0; | ||
220 | |||
221 | do { | ||
222 | DevError = 0; | ||
223 | |||
224 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */ | ||
225 | /* Recurse the subordinate bus | ||
226 | * get the subordinate bus number | ||
227 | */ | ||
228 | rc = pci_bus_read_config_byte(pci_bus, | ||
229 | PCI_DEVFN(device, function), | ||
230 | PCI_SECONDARY_BUS, &secondary_bus); | ||
231 | if (rc) { | ||
232 | return rc; | ||
233 | } else { | ||
234 | sub_bus = (int) secondary_bus; | ||
235 | |||
236 | /* Save secondary bus cfg spc with this recursive call. */ | ||
237 | rc = shpchp_save_config(ctrl, sub_bus, 0, 0); | ||
238 | if (rc) | ||
239 | return rc; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | index = 0; | ||
244 | new_slot = shpchp_slot_find(busnumber, device, index++); | ||
245 | |||
246 | dbg("new_slot = %p\n", new_slot); | ||
247 | |||
248 | while (new_slot && (new_slot->function != (u8) function)) { | ||
249 | new_slot = shpchp_slot_find(busnumber, device, index++); | ||
250 | dbg("new_slot = %p\n", new_slot); | ||
251 | } | ||
252 | if (!new_slot) { | ||
253 | /* Setup slot structure. */ | ||
254 | new_slot = shpchp_slot_create(busnumber); | ||
255 | dbg("new_slot = %p\n", new_slot); | ||
256 | |||
257 | if (new_slot == NULL) | ||
258 | return(1); | ||
259 | } | ||
260 | |||
261 | new_slot->bus = (u8) busnumber; | ||
262 | new_slot->device = (u8) device; | ||
263 | new_slot->function = (u8) function; | ||
264 | new_slot->is_a_board = 1; | ||
265 | new_slot->switch_save = 0x10; | ||
266 | new_slot->pwr_save = 1; | ||
267 | /* In case of unsupported board */ | ||
268 | new_slot->status = DevError; | ||
269 | new_slot->pci_dev = pci_find_slot(new_slot->bus, | ||
270 | (new_slot->device << 3) | new_slot->function); | ||
271 | dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev); | ||
272 | |||
273 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
274 | rc = pci_bus_read_config_dword(pci_bus, | ||
275 | PCI_DEVFN(device, function), | ||
276 | cloop << 2, | ||
277 | (u32 *) &(new_slot->config_space [cloop])); | ||
278 | /* dbg("new_slot->config_space[%x] = %x\n", | ||
279 | cloop, new_slot->config_space[cloop]); */ | ||
280 | if (rc) | ||
281 | return rc; | ||
282 | } | ||
283 | |||
284 | function++; | ||
285 | |||
286 | stop_it = 0; | ||
287 | |||
288 | /* this loop skips to the next present function | ||
289 | * reading in Class Code and Header type. | ||
290 | */ | ||
291 | |||
292 | while ((function < max_functions)&&(!stop_it)) { | ||
293 | rc = pci_bus_read_config_dword(pci_bus, | ||
294 | PCI_DEVFN(device, function), | ||
295 | PCI_VENDOR_ID, &ID); | ||
296 | |||
297 | if (ID == 0xFFFFFFFF) { /* nothing there. */ | ||
298 | function++; | ||
299 | dbg("Nothing there\n"); | ||
300 | } else { /* Something there */ | ||
301 | rc = pci_bus_read_config_byte(pci_bus, | ||
302 | PCI_DEVFN(device, function), | ||
303 | 0x0B, &class_code); | ||
304 | if (rc) | ||
305 | return rc; | ||
306 | |||
307 | rc = pci_bus_read_config_byte(pci_bus, | ||
308 | PCI_DEVFN(device, function), | ||
309 | PCI_HEADER_TYPE, &header_type); | ||
310 | if (rc) | ||
311 | return rc; | ||
312 | |||
313 | dbg("class_code = %x, header_type = %x\n", | ||
314 | class_code, header_type); | ||
315 | stop_it++; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | } while (function < max_functions); | ||
320 | /* End of IF (device in slot?) */ | ||
321 | } else if (is_hot_plug) { | ||
322 | /* Setup slot structure with entry for empty slot */ | ||
323 | new_slot = shpchp_slot_create(busnumber); | ||
324 | |||
325 | if (new_slot == NULL) { | ||
326 | return(1); | ||
327 | } | ||
328 | dbg("new_slot = %p\n", new_slot); | ||
329 | |||
330 | new_slot->bus = (u8) busnumber; | ||
331 | new_slot->device = (u8) device; | ||
332 | new_slot->function = 0; | ||
333 | new_slot->is_a_board = 0; | ||
334 | new_slot->presence_save = 0; | ||
335 | new_slot->switch_save = 0; | ||
336 | } | ||
337 | } /* End of FOR loop */ | ||
338 | |||
339 | return(0); | ||
340 | } | ||
341 | |||
342 | |||
343 | /* | ||
344 | * shpchp_save_slot_config | ||
345 | * | ||
346 | * Saves configuration info for all PCI devices in a given slot | ||
347 | * including subordinate busses. | ||
348 | * | ||
349 | * returns 0 if success | ||
350 | */ | ||
351 | int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot) | ||
352 | { | ||
353 | int rc; | ||
354 | u8 class_code; | ||
355 | u8 header_type; | ||
356 | u32 ID; | ||
357 | u8 secondary_bus; | ||
358 | int sub_bus; | ||
359 | int max_functions; | ||
360 | int function; | ||
361 | int cloop = 0; | ||
362 | int stop_it; | ||
363 | struct pci_bus lpci_bus, *pci_bus; | ||
364 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
365 | pci_bus = &lpci_bus; | ||
366 | pci_bus->number = new_slot->bus; | ||
367 | |||
368 | ID = 0xFFFFFFFF; | ||
369 | |||
370 | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
371 | PCI_VENDOR_ID, &ID); | ||
372 | |||
373 | if (ID != 0xFFFFFFFF) { /* device in slot */ | ||
374 | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
375 | 0x0B, &class_code); | ||
376 | |||
377 | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
378 | PCI_HEADER_TYPE, &header_type); | ||
379 | |||
380 | if (header_type & 0x80) /* Multi-function device */ | ||
381 | max_functions = 8; | ||
382 | else | ||
383 | max_functions = 1; | ||
384 | |||
385 | function = 0; | ||
386 | |||
387 | do { | ||
388 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
389 | /* Recurse the subordinate bus */ | ||
390 | pci_bus_read_config_byte(pci_bus, | ||
391 | PCI_DEVFN(new_slot->device, function), | ||
392 | PCI_SECONDARY_BUS, &secondary_bus); | ||
393 | |||
394 | sub_bus = (int) secondary_bus; | ||
395 | |||
396 | /* Save the config headers for the secondary bus. */ | ||
397 | rc = shpchp_save_config(ctrl, sub_bus, 0, 0); | ||
398 | |||
399 | if (rc) | ||
400 | return rc; | ||
401 | |||
402 | } /* End of IF */ | ||
403 | |||
404 | new_slot->status = 0; | ||
405 | |||
406 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
407 | pci_bus_read_config_dword(pci_bus, | ||
408 | PCI_DEVFN(new_slot->device, function), | ||
409 | cloop << 2, | ||
410 | (u32 *) &(new_slot->config_space [cloop])); | ||
411 | } | ||
412 | |||
413 | function++; | ||
414 | |||
415 | stop_it = 0; | ||
416 | |||
417 | /* this loop skips to the next present function | ||
418 | * reading in the Class Code and the Header type. | ||
419 | */ | ||
420 | |||
421 | while ((function < max_functions) && (!stop_it)) { | ||
422 | pci_bus_read_config_dword(pci_bus, | ||
423 | PCI_DEVFN(new_slot->device, function), | ||
424 | PCI_VENDOR_ID, &ID); | ||
425 | |||
426 | if (ID == 0xFFFFFFFF) { /* nothing there. */ | ||
427 | function++; | ||
428 | } else { /* Something there */ | ||
429 | pci_bus_read_config_byte(pci_bus, | ||
430 | PCI_DEVFN(new_slot->device, function), | ||
431 | 0x0B, &class_code); | ||
432 | |||
433 | pci_bus_read_config_byte(pci_bus, | ||
434 | PCI_DEVFN(new_slot->device, function), | ||
435 | PCI_HEADER_TYPE, &header_type); | ||
436 | |||
437 | stop_it++; | ||
438 | } | ||
439 | } | ||
440 | |||
441 | } while (function < max_functions); | ||
442 | } /* End of IF (device in slot?) */ | ||
443 | else { | ||
444 | return 2; | ||
445 | } | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | |||
451 | /* | ||
452 | * shpchp_save_used_resources | ||
453 | * | ||
454 | * Stores used resource information for existing boards. this is | ||
455 | * for boards that were in the system when this driver was loaded. | ||
456 | * this function is for hot plug ADD | ||
457 | * | ||
458 | * returns 0 if success | ||
459 | * if disable == 1(DISABLE_CARD), | ||
460 | * it loops for all functions of the slot and disables them. | ||
461 | * else, it just get resources of the function and return. | ||
462 | */ | ||
463 | int shpchp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable) | ||
464 | { | ||
465 | u8 cloop; | ||
466 | u8 header_type; | ||
467 | u8 secondary_bus; | ||
468 | u8 temp_byte; | ||
469 | u16 command; | ||
470 | u16 save_command; | ||
471 | u16 w_base, w_length; | ||
472 | u32 temp_register; | ||
473 | u32 save_base; | ||
474 | u32 base, length; | ||
475 | u64 base64 = 0; | ||
476 | int index = 0; | ||
477 | unsigned int devfn; | ||
478 | struct pci_resource *mem_node = NULL; | ||
479 | struct pci_resource *p_mem_node = NULL; | ||
480 | struct pci_resource *t_mem_node; | ||
481 | struct pci_resource *io_node; | ||
482 | struct pci_resource *bus_node; | ||
483 | struct pci_bus lpci_bus, *pci_bus; | ||
484 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
485 | pci_bus = &lpci_bus; | ||
486 | |||
487 | if (disable) | ||
488 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
489 | |||
490 | while ((func != NULL) && func->is_a_board) { | ||
491 | pci_bus->number = func->bus; | ||
492 | devfn = PCI_DEVFN(func->device, func->function); | ||
493 | |||
494 | /* Save the command register */ | ||
495 | pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); | ||
496 | |||
497 | if (disable) { | ||
498 | /* disable card */ | ||
499 | command = 0x00; | ||
500 | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
501 | } | ||
502 | |||
503 | /* Check for Bridge */ | ||
504 | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
505 | |||
506 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
507 | dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", | ||
508 | func->bus, func->device, save_command); | ||
509 | if (disable) { | ||
510 | /* Clear Bridge Control Register */ | ||
511 | command = 0x00; | ||
512 | pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | ||
513 | } | ||
514 | |||
515 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
516 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); | ||
517 | |||
518 | bus_node = kmalloc(sizeof(struct pci_resource), | ||
519 | GFP_KERNEL); | ||
520 | if (!bus_node) | ||
521 | return -ENOMEM; | ||
522 | |||
523 | bus_node->base = (ulong)secondary_bus; | ||
524 | bus_node->length = (ulong)(temp_byte - secondary_bus + 1); | ||
525 | |||
526 | bus_node->next = func->bus_head; | ||
527 | func->bus_head = bus_node; | ||
528 | |||
529 | /* Save IO base and Limit registers */ | ||
530 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte); | ||
531 | base = temp_byte; | ||
532 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); | ||
533 | length = temp_byte; | ||
534 | |||
535 | if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { | ||
536 | io_node = kmalloc(sizeof(struct pci_resource), | ||
537 | GFP_KERNEL); | ||
538 | if (!io_node) | ||
539 | return -ENOMEM; | ||
540 | |||
541 | io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; | ||
542 | io_node->length = (ulong)(length - base + 0x10) << 8; | ||
543 | |||
544 | io_node->next = func->io_head; | ||
545 | func->io_head = io_node; | ||
546 | } | ||
547 | |||
548 | /* Save memory base and Limit registers */ | ||
549 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); | ||
550 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); | ||
551 | |||
552 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
553 | mem_node = kmalloc(sizeof(struct pci_resource), | ||
554 | GFP_KERNEL); | ||
555 | if (!mem_node) | ||
556 | return -ENOMEM; | ||
557 | |||
558 | mem_node->base = (ulong)w_base << 16; | ||
559 | mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
560 | |||
561 | mem_node->next = func->mem_head; | ||
562 | func->mem_head = mem_node; | ||
563 | } | ||
564 | /* Save prefetchable memory base and Limit registers */ | ||
565 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); | ||
566 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); | ||
567 | |||
568 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
569 | p_mem_node = kmalloc(sizeof(struct pci_resource), | ||
570 | GFP_KERNEL); | ||
571 | if (!p_mem_node) | ||
572 | return -ENOMEM; | ||
573 | |||
574 | p_mem_node->base = (ulong)w_base << 16; | ||
575 | p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
576 | |||
577 | p_mem_node->next = func->p_mem_head; | ||
578 | func->p_mem_head = p_mem_node; | ||
579 | } | ||
580 | } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
581 | dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", | ||
582 | func->bus, func->device, save_command); | ||
583 | |||
584 | /* Figure out IO and memory base lengths */ | ||
585 | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | ||
586 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); | ||
587 | |||
588 | temp_register = 0xFFFFFFFF; | ||
589 | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | ||
590 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | ||
591 | |||
592 | if (!disable) | ||
593 | pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base); | ||
594 | |||
595 | if (!temp_register) | ||
596 | continue; | ||
597 | |||
598 | base = temp_register; | ||
599 | |||
600 | if ((base & PCI_BASE_ADDRESS_SPACE_IO) && | ||
601 | (!disable || (save_command & PCI_COMMAND_IO))) { | ||
602 | /* IO base */ | ||
603 | /* set temp_register = amount of IO space requested */ | ||
604 | base = base & 0xFFFFFFFCL; | ||
605 | base = (~base) + 1; | ||
606 | |||
607 | io_node = kmalloc(sizeof (struct pci_resource), | ||
608 | GFP_KERNEL); | ||
609 | if (!io_node) | ||
610 | return -ENOMEM; | ||
611 | |||
612 | io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; | ||
613 | io_node->length = (ulong)base; | ||
614 | dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", | ||
615 | io_node->base, io_node->length); | ||
616 | |||
617 | io_node->next = func->io_head; | ||
618 | func->io_head = io_node; | ||
619 | } else { /* map Memory */ | ||
620 | int prefetchable = 1; | ||
621 | /* struct pci_resources **res_node; */ | ||
622 | char *res_type_str = "PMEM"; | ||
623 | u32 temp_register2; | ||
624 | |||
625 | t_mem_node = kmalloc(sizeof (struct pci_resource), | ||
626 | GFP_KERNEL); | ||
627 | if (!t_mem_node) | ||
628 | return -ENOMEM; | ||
629 | |||
630 | if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && | ||
631 | (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
632 | prefetchable = 0; | ||
633 | mem_node = t_mem_node; | ||
634 | res_type_str++; | ||
635 | } else | ||
636 | p_mem_node = t_mem_node; | ||
637 | |||
638 | base = base & 0xFFFFFFF0L; | ||
639 | base = (~base) + 1; | ||
640 | |||
641 | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | ||
642 | case PCI_BASE_ADDRESS_MEM_TYPE_32: | ||
643 | if (prefetchable) { | ||
644 | p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
645 | p_mem_node->length = (ulong)base; | ||
646 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
647 | res_type_str, | ||
648 | p_mem_node->base, | ||
649 | p_mem_node->length); | ||
650 | |||
651 | p_mem_node->next = func->p_mem_head; | ||
652 | func->p_mem_head = p_mem_node; | ||
653 | } else { | ||
654 | mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
655 | mem_node->length = (ulong)base; | ||
656 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
657 | res_type_str, | ||
658 | mem_node->base, | ||
659 | mem_node->length); | ||
660 | |||
661 | mem_node->next = func->mem_head; | ||
662 | func->mem_head = mem_node; | ||
663 | } | ||
664 | break; | ||
665 | case PCI_BASE_ADDRESS_MEM_TYPE_64: | ||
666 | pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | ||
667 | base64 = temp_register2; | ||
668 | base64 = (base64 << 32) | save_base; | ||
669 | |||
670 | if (temp_register2) { | ||
671 | dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", | ||
672 | res_type_str, temp_register2, (u32)base64); | ||
673 | base64 &= 0x00000000FFFFFFFFL; | ||
674 | } | ||
675 | |||
676 | if (prefetchable) { | ||
677 | p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
678 | p_mem_node->length = base; | ||
679 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
680 | res_type_str, | ||
681 | p_mem_node->base, | ||
682 | p_mem_node->length); | ||
683 | |||
684 | p_mem_node->next = func->p_mem_head; | ||
685 | func->p_mem_head = p_mem_node; | ||
686 | } else { | ||
687 | mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
688 | mem_node->length = base; | ||
689 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
690 | res_type_str, | ||
691 | mem_node->base, | ||
692 | mem_node->length); | ||
693 | |||
694 | mem_node->next = func->mem_head; | ||
695 | func->mem_head = mem_node; | ||
696 | } | ||
697 | cloop += 4; | ||
698 | break; | ||
699 | default: | ||
700 | dbg("asur: reserved BAR type=0x%x\n", | ||
701 | temp_register); | ||
702 | break; | ||
703 | } | ||
704 | } | ||
705 | } /* End of base register loop */ | ||
706 | } else { /* Some other unknown header type */ | ||
707 | dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", | ||
708 | func->bus, func->device); | ||
709 | } | ||
710 | |||
711 | /* find the next device in this slot */ | ||
712 | if (!disable) | ||
713 | break; | ||
714 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
715 | } | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * kfree_resource_list: release memory of all list members | ||
722 | * @res: resource list to free | ||
723 | */ | ||
724 | static inline void | ||
725 | return_resource_list(struct pci_resource **func, struct pci_resource **res) | ||
726 | { | ||
727 | struct pci_resource *node; | ||
728 | struct pci_resource *t_node; | ||
729 | |||
730 | node = *func; | ||
731 | *func = NULL; | ||
732 | while (node) { | ||
733 | t_node = node->next; | ||
734 | return_resource(res, node); | ||
735 | node = t_node; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | /* | ||
740 | * shpchp_return_board_resources | ||
741 | * | ||
742 | * this routine returns all resources allocated to a board to | ||
743 | * the available pool. | ||
744 | * | ||
745 | * returns 0 if success | ||
746 | */ | ||
747 | int shpchp_return_board_resources(struct pci_func * func, | ||
748 | struct resource_lists * resources) | ||
749 | { | ||
750 | int rc; | ||
751 | dbg("%s\n", __FUNCTION__); | ||
752 | |||
753 | if (!func) | ||
754 | return 1; | ||
755 | |||
756 | return_resource_list(&(func->io_head),&(resources->io_head)); | ||
757 | return_resource_list(&(func->mem_head),&(resources->mem_head)); | ||
758 | return_resource_list(&(func->p_mem_head),&(resources->p_mem_head)); | ||
759 | return_resource_list(&(func->bus_head),&(resources->bus_head)); | ||
760 | |||
761 | rc = shpchp_resource_sort_and_combine(&(resources->mem_head)); | ||
762 | rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head)); | ||
763 | rc |= shpchp_resource_sort_and_combine(&(resources->io_head)); | ||
764 | rc |= shpchp_resource_sort_and_combine(&(resources->bus_head)); | ||
765 | |||
766 | return rc; | ||
767 | } | ||
768 | |||
769 | /** | ||
770 | * kfree_resource_list: release memory of all list members | ||
771 | * @res: resource list to free | ||
772 | */ | ||
773 | static inline void | ||
774 | kfree_resource_list(struct pci_resource **r) | ||
775 | { | ||
776 | struct pci_resource *res, *tres; | ||
777 | |||
778 | res = *r; | ||
779 | *r = NULL; | ||
780 | |||
781 | while (res) { | ||
782 | tres = res; | ||
783 | res = res->next; | ||
784 | kfree(tres); | ||
785 | } | ||
786 | } | ||
787 | |||
788 | /** | ||
789 | * shpchp_destroy_resource_list: put node back in the resource list | ||
790 | * @resources: list to put nodes back | ||
791 | */ | ||
792 | void shpchp_destroy_resource_list(struct resource_lists *resources) | ||
793 | { | ||
794 | kfree_resource_list(&(resources->io_head)); | ||
795 | kfree_resource_list(&(resources->mem_head)); | ||
796 | kfree_resource_list(&(resources->p_mem_head)); | ||
797 | kfree_resource_list(&(resources->bus_head)); | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * shpchp_destroy_board_resources: put node back in the resource list | ||
802 | * @resources: list to put nodes back | ||
803 | */ | ||
804 | void shpchp_destroy_board_resources(struct pci_func * func) | ||
805 | { | ||
806 | kfree_resource_list(&(func->io_head)); | ||
807 | kfree_resource_list(&(func->mem_head)); | ||
808 | kfree_resource_list(&(func->p_mem_head)); | ||
809 | kfree_resource_list(&(func->bus_head)); | ||
810 | } | ||