Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | * | |
8cf4c195 | 26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> |
1da177e4 LT |
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 | } |