diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/shpchp_pci.c | 861 |
1 files changed, 114 insertions, 747 deletions
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index d867099114ec..b8e95acea3b6 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c | |||
@@ -27,784 +27,151 @@ | |||
27 | * | 27 | * |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | 30 | #include <linux/module.h> |
32 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
33 | #include <linux/types.h> | 32 | #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> | 33 | #include <linux/pci.h> |
38 | #include "../pci.h" | 34 | #include "../pci.h" |
39 | #include "shpchp.h" | 35 | #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 | 36 | ||
44 | int shpchp_configure_device (struct controller* ctrl, struct pci_func* func) | 37 | void program_fw_provided_values(struct pci_dev *dev) |
45 | { | 38 | { |
46 | unsigned char bus; | 39 | u16 pci_cmd, pci_bctl; |
47 | struct pci_bus *child; | 40 | struct pci_dev *cdev; |
48 | int num; | 41 | struct hotplug_params hpp = {0x8, 0x40, 0, 0}; /* defaults */ |
49 | 42 | ||
50 | if (func->pci_dev == NULL) | 43 | /* Program hpp values for this device */ |
51 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | 44 | if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || |
52 | 45 | (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && | |
53 | /* Still NULL ? Well then scan for it ! */ | 46 | (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) |
54 | if (func->pci_dev == NULL) { | 47 | return; |
55 | num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function)); | 48 | |
56 | if (num) { | 49 | get_hp_params_from_firmware(dev, &hpp); |
57 | dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate, | 50 | |
58 | ctrl->pci_dev->subordinate->number); | 51 | pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp.cache_line_size); |
59 | pci_bus_add_devices(ctrl->pci_dev->subordinate); | 52 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp.latency_timer); |
60 | } | 53 | pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); |
61 | 54 | if (hpp.enable_serr) | |
62 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | 55 | pci_cmd |= PCI_COMMAND_SERR; |
63 | if (func->pci_dev == NULL) { | 56 | else |
64 | dbg("ERROR: pci_dev still null\n"); | 57 | pci_cmd &= ~PCI_COMMAND_SERR; |
65 | return 0; | 58 | if (hpp.enable_perr) |
59 | pci_cmd |= PCI_COMMAND_PARITY; | ||
60 | else | ||
61 | pci_cmd &= ~PCI_COMMAND_PARITY; | ||
62 | pci_write_config_word(dev, PCI_COMMAND, pci_cmd); | ||
63 | |||
64 | /* Program bridge control value and child devices */ | ||
65 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { | ||
66 | pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, | ||
67 | hpp.latency_timer); | ||
68 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); | ||
69 | if (hpp.enable_serr) | ||
70 | pci_bctl |= PCI_BRIDGE_CTL_SERR; | ||
71 | else | ||
72 | pci_bctl &= ~PCI_BRIDGE_CTL_SERR; | ||
73 | if (hpp.enable_perr) | ||
74 | pci_bctl |= PCI_BRIDGE_CTL_PARITY; | ||
75 | else | ||
76 | pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; | ||
77 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); | ||
78 | if (dev->subordinate) { | ||
79 | list_for_each_entry(cdev, &dev->subordinate->devices, | ||
80 | bus_list) | ||
81 | program_fw_provided_values(cdev); | ||
66 | } | 82 | } |
67 | } | 83 | } |
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 | } | 84 | } |
78 | 85 | ||
79 | 86 | int shpchp_configure_device(struct slot *p_slot) | |
80 | int shpchp_unconfigure_device(struct pci_func* func) | ||
81 | { | 87 | { |
82 | int rc = 0; | 88 | struct pci_dev *dev; |
83 | int j; | 89 | struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; |
84 | 90 | int num, fn; | |
85 | dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, | 91 | |
86 | func->device, func->function); | 92 | dev = pci_find_slot(p_slot->bus, PCI_DEVFN(p_slot->device, 0)); |
87 | 93 | if (dev) { | |
88 | for (j=0; j<8 ; j++) { | 94 | err("Device %s already exists at %x:%x, cannot hot-add\n", |
89 | struct pci_dev* temp = pci_find_slot(func->bus, | 95 | pci_name(dev), p_slot->bus, p_slot->device); |
90 | (func->device << 3) | j); | 96 | return -EINVAL; |
91 | if (temp) { | ||
92 | pci_remove_bus_device(temp); | ||
93 | } | ||
94 | } | 97 | } |
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 | 98 | ||
113 | fakedev.devfn = dev_num << 3; | 99 | num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0)); |
114 | fakedev.bus = &fakebus; | 100 | if (num == 0) { |
115 | fakebus.number = bus_num; | 101 | err("No new device found\n"); |
116 | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | 102 | return -ENODEV; |
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 | } | 103 | } |
189 | 104 | ||
190 | dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, | 105 | for (fn = 0; fn < 8; fn++) { |
191 | LastSupported); | 106 | if (!(dev = pci_find_slot(p_slot->bus, |
192 | 107 | PCI_DEVFN(p_slot->device, fn)))) | |
193 | /* Save PCI configuration space for all devices in supported slots */ | 108 | continue; |
194 | pci_bus->number = busnumber; | 109 | if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { |
195 | for (device = FirstSupported; device <= LastSupported; device++) { | 110 | err("Cannot hot-add display device %s\n", |
196 | ID = 0xFFFFFFFF; | 111 | pci_name(dev)); |
197 | rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), | 112 | continue; |
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 | } | 113 | } |
337 | } /* End of FOR loop */ | 114 | if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || |
338 | 115 | (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { | |
339 | return(0); | 116 | /* Find an unused bus number for the new bridge */ |
340 | } | 117 | struct pci_bus *child; |
341 | 118 | unsigned char busnr, start = parent->secondary; | |
342 | 119 | unsigned char end = parent->subordinate; | |
343 | /* | 120 | for (busnr = start; busnr <= end; busnr++) { |
344 | * shpchp_save_slot_config | 121 | if (!pci_find_bus(pci_domain_nr(parent), |
345 | * | 122 | busnr)) |
346 | * Saves configuration info for all PCI devices in a given slot | 123 | break; |
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 | } | 124 | } |
412 | 125 | if (busnr >= end) { | |
413 | function++; | 126 | err("No free bus for hot-added bridge\n"); |
414 | 127 | continue; | |
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 | } | 128 | } |
440 | 129 | child = pci_add_new_bus(parent, dev, busnr); | |
441 | } while (function < max_functions); | 130 | if (!child) { |
442 | } /* End of IF (device in slot?) */ | 131 | err("Cannot add new bus for %s\n", |
443 | else { | 132 | pci_name(dev)); |
444 | return 2; | 133 | continue; |
134 | } | ||
135 | child->subordinate = pci_do_scan_bus(child); | ||
136 | pci_bus_size_bridges(child); | ||
137 | } | ||
138 | program_fw_provided_values(dev); | ||
445 | } | 139 | } |
446 | 140 | ||
141 | pci_bus_assign_resources(parent); | ||
142 | pci_bus_add_devices(parent); | ||
143 | pci_enable_bridges(parent); | ||
447 | return 0; | 144 | return 0; |
448 | } | 145 | } |
449 | 146 | ||
450 | 147 | int shpchp_unconfigure_device(struct slot *p_slot) | |
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 | { | 148 | { |
465 | u8 cloop; | 149 | int rc = 0; |
466 | u8 header_type; | 150 | int j; |
467 | u8 secondary_bus; | 151 | u8 bctl = 0; |
468 | u8 temp_byte; | 152 | |
469 | u16 command; | 153 | dbg("%s: bus/dev = %x/%x\n", __FUNCTION__, p_slot->bus, p_slot->device); |
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 | 154 | ||
497 | if (disable) { | 155 | for (j=0; j<8 ; j++) { |
498 | /* disable card */ | 156 | struct pci_dev* temp = pci_find_slot(p_slot->bus, |
499 | command = 0x00; | 157 | (p_slot->device << 3) | j); |
500 | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | 158 | if (!temp) |
159 | continue; | ||
160 | if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) { | ||
161 | err("Cannot remove display device %s\n", | ||
162 | pci_name(temp)); | ||
163 | continue; | ||
501 | } | 164 | } |
502 | 165 | if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | |
503 | /* Check for Bridge */ | 166 | pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); |
504 | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | 167 | if (bctl & PCI_BRIDGE_CTL_VGA) { |
505 | 168 | err("Cannot remove display device %s\n", | |
506 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | 169 | pci_name(temp)); |
507 | dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", | 170 | continue; |
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 | } | 171 | } |
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 | } | 172 | } |
710 | 173 | pci_remove_bus_device(temp); | |
711 | /* find the next device in this slot */ | ||
712 | if (!disable) | ||
713 | break; | ||
714 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
715 | } | 174 | } |
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; | 175 | return rc; |
767 | } | 176 | } |
768 | 177 | ||
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 | } | ||