1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Compaq Hot Plug Controller Driver |
4 | * |
5 | * Copyright (C) 1995,2001 Compaq Computer Corporation |
6 | * Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com> |
7 | * Copyright (C) 2001 IBM Corp. |
8 | * |
9 | * All rights reserved. |
10 | * |
11 | * Send feedback to <greg@kroah.com> |
12 | * |
13 | * Jan 12, 2003 - Added 66/100/133MHz PCI-X support, |
14 | * Torben Mathiasen <torben.mathiasen@hp.com> |
15 | */ |
16 | |
17 | #include <linux/module.h> |
18 | #include <linux/moduleparam.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/types.h> |
21 | #include <linux/proc_fs.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/workqueue.h> |
24 | #include <linux/pci.h> |
25 | #include <linux/pci_hotplug.h> |
26 | #include <linux/init.h> |
27 | #include <linux/interrupt.h> |
28 | |
29 | #include <linux/uaccess.h> |
30 | |
31 | #include "cpqphp.h" |
32 | #include "cpqphp_nvram.h" |
33 | |
34 | |
35 | /* Global variables */ |
36 | int cpqhp_debug; |
37 | int cpqhp_legacy_mode; |
38 | struct controller *cpqhp_ctrl_list; /* = NULL */ |
39 | struct pci_func *cpqhp_slot_list[256]; |
40 | struct irq_routing_table *cpqhp_routing_table; |
41 | |
42 | /* local variables */ |
43 | static void __iomem *smbios_table; |
44 | static void __iomem *smbios_start; |
45 | static void __iomem *cpqhp_rom_start; |
46 | static bool power_mode; |
47 | static bool debug; |
48 | static int initialized; |
49 | |
50 | #define DRIVER_VERSION "0.9.8" |
51 | #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>" |
52 | #define DRIVER_DESC "Compaq Hot Plug PCI Controller Driver" |
53 | |
54 | MODULE_AUTHOR(DRIVER_AUTHOR); |
55 | MODULE_DESCRIPTION(DRIVER_DESC); |
56 | MODULE_LICENSE("GPL" ); |
57 | |
58 | module_param(power_mode, bool, 0644); |
59 | MODULE_PARM_DESC(power_mode, "Power mode enabled or not" ); |
60 | |
61 | module_param(debug, bool, 0644); |
62 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not" ); |
63 | |
64 | #define CPQHPC_MODULE_MINOR 208 |
65 | |
66 | static inline int is_slot64bit(struct slot *slot) |
67 | { |
68 | return (readb(addr: slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0; |
69 | } |
70 | |
71 | static inline int is_slot66mhz(struct slot *slot) |
72 | { |
73 | return (readb(addr: slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) ? 1 : 0; |
74 | } |
75 | |
76 | /** |
77 | * detect_SMBIOS_pointer - find the System Management BIOS Table in mem region. |
78 | * @begin: begin pointer for region to be scanned. |
79 | * @end: end pointer for region to be scanned. |
80 | * |
81 | * Returns pointer to the head of the SMBIOS tables (or %NULL). |
82 | */ |
83 | static void __iomem *detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end) |
84 | { |
85 | void __iomem *fp; |
86 | void __iomem *endp; |
87 | u8 temp1, temp2, temp3, temp4; |
88 | int status = 0; |
89 | |
90 | endp = (end - sizeof(u32) + 1); |
91 | |
92 | for (fp = begin; fp <= endp; fp += 16) { |
93 | temp1 = readb(addr: fp); |
94 | temp2 = readb(addr: fp+1); |
95 | temp3 = readb(addr: fp+2); |
96 | temp4 = readb(addr: fp+3); |
97 | if (temp1 == '_' && |
98 | temp2 == 'S' && |
99 | temp3 == 'M' && |
100 | temp4 == '_') { |
101 | status = 1; |
102 | break; |
103 | } |
104 | } |
105 | |
106 | if (!status) |
107 | fp = NULL; |
108 | |
109 | dbg("Discovered SMBIOS Entry point at %p\n" , fp); |
110 | |
111 | return fp; |
112 | } |
113 | |
114 | /** |
115 | * init_SERR - Initializes the per slot SERR generation. |
116 | * @ctrl: controller to use |
117 | * |
118 | * For unexpected switch opens |
119 | */ |
120 | static int init_SERR(struct controller *ctrl) |
121 | { |
122 | u32 tempdword; |
123 | u32 number_of_slots; |
124 | |
125 | if (!ctrl) |
126 | return 1; |
127 | |
128 | tempdword = ctrl->first_slot; |
129 | |
130 | number_of_slots = readb(addr: ctrl->hpc_reg + SLOT_MASK) & 0x0F; |
131 | /* Loop through slots */ |
132 | while (number_of_slots) { |
133 | writeb(val: 0, addr: ctrl->hpc_reg + SLOT_SERR); |
134 | tempdword++; |
135 | number_of_slots--; |
136 | } |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static int init_cpqhp_routing_table(void) |
142 | { |
143 | int len; |
144 | |
145 | cpqhp_routing_table = pcibios_get_irq_routing_table(); |
146 | if (cpqhp_routing_table == NULL) |
147 | return -ENOMEM; |
148 | |
149 | len = cpqhp_routing_table_length(); |
150 | if (len == 0) { |
151 | kfree(objp: cpqhp_routing_table); |
152 | cpqhp_routing_table = NULL; |
153 | return -1; |
154 | } |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | /* nice debugging output */ |
160 | static void pci_print_IRQ_route(void) |
161 | { |
162 | int len; |
163 | int loop; |
164 | u8 tbus, tdevice, tslot; |
165 | |
166 | len = cpqhp_routing_table_length(); |
167 | |
168 | dbg("bus dev func slot\n" ); |
169 | for (loop = 0; loop < len; ++loop) { |
170 | tbus = cpqhp_routing_table->slots[loop].bus; |
171 | tdevice = cpqhp_routing_table->slots[loop].devfn; |
172 | tslot = cpqhp_routing_table->slots[loop].slot; |
173 | dbg("%d %d %d %d\n" , tbus, tdevice >> 3, tdevice & 0x7, tslot); |
174 | |
175 | } |
176 | } |
177 | |
178 | |
179 | /** |
180 | * get_subsequent_smbios_entry: get the next entry from bios table. |
181 | * @smbios_start: where to start in the SMBIOS table |
182 | * @smbios_table: location of the SMBIOS table |
183 | * @curr: %NULL or pointer to previously returned structure |
184 | * |
185 | * Gets the first entry if previous == NULL; |
186 | * otherwise, returns the next entry. |
187 | * Uses global SMBIOS Table pointer. |
188 | * |
189 | * Returns a pointer to an SMBIOS structure or NULL if none found. |
190 | */ |
191 | static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start, |
192 | void __iomem *smbios_table, |
193 | void __iomem *curr) |
194 | { |
195 | u8 bail = 0; |
196 | u8 previous_byte = 1; |
197 | void __iomem *p_temp; |
198 | void __iomem *p_max; |
199 | |
200 | if (!smbios_table || !curr) |
201 | return NULL; |
202 | |
203 | /* set p_max to the end of the table */ |
204 | p_max = smbios_start + readw(addr: smbios_table + ST_LENGTH); |
205 | |
206 | p_temp = curr; |
207 | p_temp += readb(addr: curr + SMBIOS_GENERIC_LENGTH); |
208 | |
209 | while ((p_temp < p_max) && !bail) { |
210 | /* Look for the double NULL terminator |
211 | * The first condition is the previous byte |
212 | * and the second is the curr |
213 | */ |
214 | if (!previous_byte && !(readb(addr: p_temp))) |
215 | bail = 1; |
216 | |
217 | previous_byte = readb(addr: p_temp); |
218 | p_temp++; |
219 | } |
220 | |
221 | if (p_temp < p_max) |
222 | return p_temp; |
223 | else |
224 | return NULL; |
225 | } |
226 | |
227 | |
228 | /** |
229 | * get_SMBIOS_entry - return the requested SMBIOS entry or %NULL |
230 | * @smbios_start: where to start in the SMBIOS table |
231 | * @smbios_table: location of the SMBIOS table |
232 | * @type: SMBIOS structure type to be returned |
233 | * @previous: %NULL or pointer to previously returned structure |
234 | * |
235 | * Gets the first entry of the specified type if previous == %NULL; |
236 | * Otherwise, returns the next entry of the given type. |
237 | * Uses global SMBIOS Table pointer. |
238 | * Uses get_subsequent_smbios_entry. |
239 | * |
240 | * Returns a pointer to an SMBIOS structure or %NULL if none found. |
241 | */ |
242 | static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start, |
243 | void __iomem *smbios_table, |
244 | u8 type, |
245 | void __iomem *previous) |
246 | { |
247 | if (!smbios_table) |
248 | return NULL; |
249 | |
250 | if (!previous) |
251 | previous = smbios_start; |
252 | else |
253 | previous = get_subsequent_smbios_entry(smbios_start, |
254 | smbios_table, curr: previous); |
255 | |
256 | while (previous) |
257 | if (readb(addr: previous + SMBIOS_GENERIC_TYPE) != type) |
258 | previous = get_subsequent_smbios_entry(smbios_start, |
259 | smbios_table, curr: previous); |
260 | else |
261 | break; |
262 | |
263 | return previous; |
264 | } |
265 | |
266 | static int ctrl_slot_cleanup(struct controller *ctrl) |
267 | { |
268 | struct slot *old_slot, *next_slot; |
269 | |
270 | old_slot = ctrl->slot; |
271 | ctrl->slot = NULL; |
272 | |
273 | while (old_slot) { |
274 | next_slot = old_slot->next; |
275 | pci_hp_deregister(slot: &old_slot->hotplug_slot); |
276 | kfree(objp: old_slot); |
277 | old_slot = next_slot; |
278 | } |
279 | |
280 | cpqhp_remove_debugfs_files(ctrl); |
281 | |
282 | /* Free IRQ associated with hot plug device */ |
283 | free_irq(ctrl->interrupt, ctrl); |
284 | /* Unmap the memory */ |
285 | iounmap(addr: ctrl->hpc_reg); |
286 | /* Finally reclaim PCI mem */ |
287 | release_mem_region(pci_resource_start(ctrl->pci_dev, 0), |
288 | pci_resource_len(ctrl->pci_dev, 0)); |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | |
294 | /** |
295 | * get_slot_mapping - determine logical slot mapping for PCI device |
296 | * |
297 | * Won't work for more than one PCI-PCI bridge in a slot. |
298 | * |
299 | * @bus: pointer to the PCI bus structure |
300 | * @bus_num: bus number of PCI device |
301 | * @dev_num: device number of PCI device |
302 | * @slot: Pointer to u8 where slot number will be returned |
303 | * |
304 | * Output: SUCCESS or FAILURE |
305 | */ |
306 | static int |
307 | get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) |
308 | { |
309 | u32 work; |
310 | long len; |
311 | long loop; |
312 | |
313 | u8 tbus, tdevice, tslot, bridgeSlot; |
314 | |
315 | dbg("%s: %p, %d, %d, %p\n" , __func__, bus, bus_num, dev_num, slot); |
316 | |
317 | bridgeSlot = 0xFF; |
318 | |
319 | len = cpqhp_routing_table_length(); |
320 | for (loop = 0; loop < len; ++loop) { |
321 | tbus = cpqhp_routing_table->slots[loop].bus; |
322 | tdevice = cpqhp_routing_table->slots[loop].devfn >> 3; |
323 | tslot = cpqhp_routing_table->slots[loop].slot; |
324 | |
325 | if ((tbus == bus_num) && (tdevice == dev_num)) { |
326 | *slot = tslot; |
327 | return 0; |
328 | } else { |
329 | /* Did not get a match on the target PCI device. Check |
330 | * if the current IRQ table entry is a PCI-to-PCI |
331 | * bridge device. If so, and it's secondary bus |
332 | * matches the bus number for the target device, I need |
333 | * to save the bridge's slot number. If I can not find |
334 | * an entry for the target device, I will have to |
335 | * assume it's on the other side of the bridge, and |
336 | * assign it the bridge's slot. |
337 | */ |
338 | bus->number = tbus; |
339 | pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0), |
340 | PCI_CLASS_REVISION, val: &work); |
341 | |
342 | if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { |
343 | pci_bus_read_config_dword(bus, |
344 | PCI_DEVFN(tdevice, 0), |
345 | PCI_PRIMARY_BUS, val: &work); |
346 | // See if bridge's secondary bus matches target bus. |
347 | if (((work >> 8) & 0x000000FF) == (long) bus_num) |
348 | bridgeSlot = tslot; |
349 | } |
350 | } |
351 | |
352 | } |
353 | |
354 | /* If we got here, we didn't find an entry in the IRQ mapping table for |
355 | * the target PCI device. If we did determine that the target device |
356 | * is on the other side of a PCI-to-PCI bridge, return the slot number |
357 | * for the bridge. |
358 | */ |
359 | if (bridgeSlot != 0xFF) { |
360 | *slot = bridgeSlot; |
361 | return 0; |
362 | } |
363 | /* Couldn't find an entry in the routing table for this PCI device */ |
364 | return -1; |
365 | } |
366 | |
367 | |
368 | /** |
369 | * cpqhp_set_attention_status - Turns the Amber LED for a slot on or off |
370 | * @ctrl: struct controller to use |
371 | * @func: PCI device/function info |
372 | * @status: LED control flag: 1 = LED on, 0 = LED off |
373 | */ |
374 | static int |
375 | cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func, |
376 | u32 status) |
377 | { |
378 | u8 hp_slot; |
379 | |
380 | if (func == NULL) |
381 | return 1; |
382 | |
383 | hp_slot = func->device - ctrl->slot_device_offset; |
384 | |
385 | /* Wait for exclusive access to hardware */ |
386 | mutex_lock(&ctrl->crit_sect); |
387 | |
388 | if (status == 1) |
389 | amber_LED_on(ctrl, slot: hp_slot); |
390 | else if (status == 0) |
391 | amber_LED_off(ctrl, slot: hp_slot); |
392 | else { |
393 | /* Done with exclusive hardware access */ |
394 | mutex_unlock(lock: &ctrl->crit_sect); |
395 | return 1; |
396 | } |
397 | |
398 | set_SOGO(ctrl); |
399 | |
400 | /* Wait for SOBS to be unset */ |
401 | wait_for_ctrl_irq(ctrl); |
402 | |
403 | /* Done with exclusive hardware access */ |
404 | mutex_unlock(lock: &ctrl->crit_sect); |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | |
410 | /** |
411 | * set_attention_status - Turns the Amber LED for a slot on or off |
412 | * @hotplug_slot: slot to change LED on |
413 | * @status: LED control flag |
414 | */ |
415 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) |
416 | { |
417 | struct pci_func *slot_func; |
418 | struct slot *slot = to_slot(hotplug_slot); |
419 | struct controller *ctrl = slot->ctrl; |
420 | u8 bus; |
421 | u8 devfn; |
422 | u8 device; |
423 | u8 function; |
424 | |
425 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
426 | |
427 | if (cpqhp_get_bus_dev(ctrl, bus_num: &bus, dev_num: &devfn, slot: slot->number) == -1) |
428 | return -ENODEV; |
429 | |
430 | device = devfn >> 3; |
431 | function = devfn & 0x7; |
432 | dbg("bus, dev, fn = %d, %d, %d\n" , bus, device, function); |
433 | |
434 | slot_func = cpqhp_slot_find(bus, device, index: function); |
435 | if (!slot_func) |
436 | return -ENODEV; |
437 | |
438 | return cpqhp_set_attention_status(ctrl, func: slot_func, status); |
439 | } |
440 | |
441 | |
442 | static int process_SI(struct hotplug_slot *hotplug_slot) |
443 | { |
444 | struct pci_func *slot_func; |
445 | struct slot *slot = to_slot(hotplug_slot); |
446 | struct controller *ctrl = slot->ctrl; |
447 | u8 bus; |
448 | u8 devfn; |
449 | u8 device; |
450 | u8 function; |
451 | |
452 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
453 | |
454 | if (cpqhp_get_bus_dev(ctrl, bus_num: &bus, dev_num: &devfn, slot: slot->number) == -1) |
455 | return -ENODEV; |
456 | |
457 | device = devfn >> 3; |
458 | function = devfn & 0x7; |
459 | dbg("bus, dev, fn = %d, %d, %d\n" , bus, device, function); |
460 | |
461 | slot_func = cpqhp_slot_find(bus, device, index: function); |
462 | if (!slot_func) |
463 | return -ENODEV; |
464 | |
465 | slot_func->bus = bus; |
466 | slot_func->device = device; |
467 | slot_func->function = function; |
468 | slot_func->configured = 0; |
469 | dbg("board_added(%p, %p)\n" , slot_func, ctrl); |
470 | return cpqhp_process_SI(ctrl, func: slot_func); |
471 | } |
472 | |
473 | |
474 | static int process_SS(struct hotplug_slot *hotplug_slot) |
475 | { |
476 | struct pci_func *slot_func; |
477 | struct slot *slot = to_slot(hotplug_slot); |
478 | struct controller *ctrl = slot->ctrl; |
479 | u8 bus; |
480 | u8 devfn; |
481 | u8 device; |
482 | u8 function; |
483 | |
484 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
485 | |
486 | if (cpqhp_get_bus_dev(ctrl, bus_num: &bus, dev_num: &devfn, slot: slot->number) == -1) |
487 | return -ENODEV; |
488 | |
489 | device = devfn >> 3; |
490 | function = devfn & 0x7; |
491 | dbg("bus, dev, fn = %d, %d, %d\n" , bus, device, function); |
492 | |
493 | slot_func = cpqhp_slot_find(bus, device, index: function); |
494 | if (!slot_func) |
495 | return -ENODEV; |
496 | |
497 | dbg("In %s, slot_func = %p, ctrl = %p\n" , __func__, slot_func, ctrl); |
498 | return cpqhp_process_SS(ctrl, func: slot_func); |
499 | } |
500 | |
501 | |
502 | static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) |
503 | { |
504 | struct slot *slot = to_slot(hotplug_slot); |
505 | struct controller *ctrl = slot->ctrl; |
506 | |
507 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
508 | |
509 | return cpqhp_hardware_test(ctrl, test_num: value); |
510 | } |
511 | |
512 | |
513 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) |
514 | { |
515 | struct slot *slot = to_slot(hotplug_slot); |
516 | struct controller *ctrl = slot->ctrl; |
517 | |
518 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
519 | |
520 | *value = get_slot_enabled(ctrl, slot); |
521 | return 0; |
522 | } |
523 | |
524 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) |
525 | { |
526 | struct slot *slot = to_slot(hotplug_slot); |
527 | struct controller *ctrl = slot->ctrl; |
528 | |
529 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
530 | |
531 | *value = cpq_get_attention_status(ctrl, slot); |
532 | return 0; |
533 | } |
534 | |
535 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) |
536 | { |
537 | struct slot *slot = to_slot(hotplug_slot); |
538 | struct controller *ctrl = slot->ctrl; |
539 | |
540 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
541 | |
542 | *value = cpq_get_latch_status(ctrl, slot); |
543 | |
544 | return 0; |
545 | } |
546 | |
547 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) |
548 | { |
549 | struct slot *slot = to_slot(hotplug_slot); |
550 | struct controller *ctrl = slot->ctrl; |
551 | |
552 | dbg("%s - physical_slot = %s\n" , __func__, slot_name(slot)); |
553 | |
554 | *value = get_presence_status(ctrl, slot); |
555 | |
556 | return 0; |
557 | } |
558 | |
559 | static const struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { |
560 | .set_attention_status = set_attention_status, |
561 | .enable_slot = process_SI, |
562 | .disable_slot = process_SS, |
563 | .hardware_test = hardware_test, |
564 | .get_power_status = get_power_status, |
565 | .get_attention_status = get_attention_status, |
566 | .get_latch_status = get_latch_status, |
567 | .get_adapter_status = get_adapter_status, |
568 | }; |
569 | |
570 | #define SLOT_NAME_SIZE 10 |
571 | |
572 | static int ctrl_slot_setup(struct controller *ctrl, |
573 | void __iomem *smbios_start, |
574 | void __iomem *smbios_table) |
575 | { |
576 | struct slot *slot; |
577 | struct pci_bus *bus = ctrl->pci_bus; |
578 | u8 number_of_slots; |
579 | u8 slot_device; |
580 | u8 slot_number; |
581 | u8 ctrl_slot; |
582 | u32 tempdword; |
583 | char name[SLOT_NAME_SIZE]; |
584 | void __iomem *slot_entry = NULL; |
585 | int result; |
586 | |
587 | dbg("%s\n" , __func__); |
588 | |
589 | tempdword = readl(addr: ctrl->hpc_reg + INT_INPUT_CLEAR); |
590 | |
591 | number_of_slots = readb(addr: ctrl->hpc_reg + SLOT_MASK) & 0x0F; |
592 | slot_device = readb(addr: ctrl->hpc_reg + SLOT_MASK) >> 4; |
593 | slot_number = ctrl->first_slot; |
594 | |
595 | while (number_of_slots) { |
596 | slot = kzalloc(size: sizeof(*slot), GFP_KERNEL); |
597 | if (!slot) { |
598 | result = -ENOMEM; |
599 | goto error; |
600 | } |
601 | |
602 | slot->ctrl = ctrl; |
603 | slot->bus = ctrl->bus; |
604 | slot->device = slot_device; |
605 | slot->number = slot_number; |
606 | dbg("slot->number = %u\n" , slot->number); |
607 | |
608 | slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, type: 9, |
609 | previous: slot_entry); |
610 | |
611 | while (slot_entry && (readw(addr: slot_entry + SMBIOS_SLOT_NUMBER) != |
612 | slot->number)) { |
613 | slot_entry = get_SMBIOS_entry(smbios_start, |
614 | smbios_table, type: 9, previous: slot_entry); |
615 | } |
616 | |
617 | slot->p_sm_slot = slot_entry; |
618 | |
619 | timer_setup(&slot->task_event, cpqhp_pushbutton_thread, 0); |
620 | slot->task_event.expires = jiffies + 5 * HZ; |
621 | |
622 | /*FIXME: these capabilities aren't used but if they are |
623 | * they need to be correctly implemented |
624 | */ |
625 | slot->capabilities |= PCISLOT_REPLACE_SUPPORTED; |
626 | slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED; |
627 | |
628 | if (is_slot64bit(slot)) |
629 | slot->capabilities |= PCISLOT_64_BIT_SUPPORTED; |
630 | if (is_slot66mhz(slot)) |
631 | slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED; |
632 | if (bus->cur_bus_speed == PCI_SPEED_66MHz) |
633 | slot->capabilities |= PCISLOT_66_MHZ_OPERATION; |
634 | |
635 | ctrl_slot = |
636 | slot_device - (readb(addr: ctrl->hpc_reg + SLOT_MASK) >> 4); |
637 | |
638 | /* Check presence */ |
639 | slot->capabilities |= |
640 | ((((~tempdword) >> 23) | |
641 | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; |
642 | /* Check the switch state */ |
643 | slot->capabilities |= |
644 | ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; |
645 | /* Check the slot enable */ |
646 | slot->capabilities |= |
647 | ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; |
648 | |
649 | /* register this slot with the hotplug pci core */ |
650 | snprintf(buf: name, SLOT_NAME_SIZE, fmt: "%u" , slot->number); |
651 | slot->hotplug_slot.ops = &cpqphp_hotplug_slot_ops; |
652 | |
653 | dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n" , |
654 | slot->bus, slot->device, |
655 | slot->number, ctrl->slot_device_offset, |
656 | slot_number); |
657 | result = pci_hp_register(&slot->hotplug_slot, |
658 | ctrl->pci_dev->bus, |
659 | slot->device, |
660 | name); |
661 | if (result) { |
662 | err("pci_hp_register failed with error %d\n" , result); |
663 | goto error_slot; |
664 | } |
665 | |
666 | slot->next = ctrl->slot; |
667 | ctrl->slot = slot; |
668 | |
669 | number_of_slots--; |
670 | slot_device++; |
671 | slot_number++; |
672 | } |
673 | |
674 | return 0; |
675 | error_slot: |
676 | kfree(objp: slot); |
677 | error: |
678 | return result; |
679 | } |
680 | |
681 | static int one_time_init(void) |
682 | { |
683 | int loop; |
684 | int retval = 0; |
685 | |
686 | if (initialized) |
687 | return 0; |
688 | |
689 | power_mode = 0; |
690 | |
691 | retval = init_cpqhp_routing_table(); |
692 | if (retval) |
693 | goto error; |
694 | |
695 | if (cpqhp_debug) |
696 | pci_print_IRQ_route(); |
697 | |
698 | dbg("Initialize + Start the notification mechanism\n" ); |
699 | |
700 | retval = cpqhp_event_start_thread(); |
701 | if (retval) |
702 | goto error; |
703 | |
704 | dbg("Initialize slot lists\n" ); |
705 | for (loop = 0; loop < 256; loop++) |
706 | cpqhp_slot_list[loop] = NULL; |
707 | |
708 | /* FIXME: We also need to hook the NMI handler eventually. |
709 | * this also needs to be worked with Christoph |
710 | * register_NMI_handler(); |
711 | */ |
712 | /* Map rom address */ |
713 | cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); |
714 | if (!cpqhp_rom_start) { |
715 | err("Could not ioremap memory region for ROM\n" ); |
716 | retval = -EIO; |
717 | goto error; |
718 | } |
719 | |
720 | /* Now, map the int15 entry point if we are on compaq specific |
721 | * hardware |
722 | */ |
723 | compaq_nvram_init(rom_start: cpqhp_rom_start); |
724 | |
725 | /* Map smbios table entry point structure */ |
726 | smbios_table = detect_SMBIOS_pointer(begin: cpqhp_rom_start, |
727 | end: cpqhp_rom_start + ROM_PHY_LEN); |
728 | if (!smbios_table) { |
729 | err("Could not find the SMBIOS pointer in memory\n" ); |
730 | retval = -EIO; |
731 | goto error_rom_start; |
732 | } |
733 | |
734 | smbios_start = ioremap(readl(addr: smbios_table + ST_ADDRESS), |
735 | readw(addr: smbios_table + ST_LENGTH)); |
736 | if (!smbios_start) { |
737 | err("Could not ioremap memory region taken from SMBIOS values\n" ); |
738 | retval = -EIO; |
739 | goto error_smbios_start; |
740 | } |
741 | |
742 | initialized = 1; |
743 | |
744 | return retval; |
745 | |
746 | error_smbios_start: |
747 | iounmap(addr: smbios_start); |
748 | error_rom_start: |
749 | iounmap(addr: cpqhp_rom_start); |
750 | error: |
751 | return retval; |
752 | } |
753 | |
754 | static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
755 | { |
756 | u8 num_of_slots = 0; |
757 | u8 hp_slot = 0; |
758 | u8 device; |
759 | u8 bus_cap; |
760 | u16 temp_word; |
761 | u16 vendor_id; |
762 | u16 subsystem_vid; |
763 | u16 subsystem_deviceid; |
764 | u32 rc; |
765 | struct controller *ctrl; |
766 | struct pci_func *func; |
767 | struct pci_bus *bus; |
768 | int err; |
769 | |
770 | err = pci_enable_device(dev: pdev); |
771 | if (err) { |
772 | printk(KERN_ERR MY_NAME ": cannot enable PCI device %s (%d)\n" , |
773 | pci_name(pdev), err); |
774 | return err; |
775 | } |
776 | |
777 | bus = pdev->subordinate; |
778 | if (!bus) { |
779 | pci_notice(pdev, "the device is not a bridge, skipping\n" ); |
780 | rc = -ENODEV; |
781 | goto err_disable_device; |
782 | } |
783 | |
784 | /* Need to read VID early b/c it's used to differentiate CPQ and INTC |
785 | * discovery |
786 | */ |
787 | vendor_id = pdev->vendor; |
788 | if ((vendor_id != PCI_VENDOR_ID_COMPAQ) && |
789 | (vendor_id != PCI_VENDOR_ID_INTEL)) { |
790 | err(msg_HPC_non_compaq_or_intel); |
791 | rc = -ENODEV; |
792 | goto err_disable_device; |
793 | } |
794 | dbg("Vendor ID: %x\n" , vendor_id); |
795 | |
796 | dbg("revision: %d\n" , pdev->revision); |
797 | if ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!pdev->revision)) { |
798 | err(msg_HPC_rev_error); |
799 | rc = -ENODEV; |
800 | goto err_disable_device; |
801 | } |
802 | |
803 | /* Check for the proper subsystem IDs |
804 | * Intel uses a different SSID programming model than Compaq. |
805 | * For Intel, each SSID bit identifies a PHP capability. |
806 | * Also Intel HPCs may have RID=0. |
807 | */ |
808 | if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) { |
809 | err(msg_HPC_not_supported); |
810 | rc = -ENODEV; |
811 | goto err_disable_device; |
812 | } |
813 | |
814 | /* TODO: This code can be made to support non-Compaq or Intel |
815 | * subsystem IDs |
816 | */ |
817 | subsystem_vid = pdev->subsystem_vendor; |
818 | dbg("Subsystem Vendor ID: %x\n" , subsystem_vid); |
819 | if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) { |
820 | err(msg_HPC_non_compaq_or_intel); |
821 | rc = -ENODEV; |
822 | goto err_disable_device; |
823 | } |
824 | |
825 | ctrl = kzalloc(size: sizeof(struct controller), GFP_KERNEL); |
826 | if (!ctrl) { |
827 | rc = -ENOMEM; |
828 | goto err_disable_device; |
829 | } |
830 | |
831 | subsystem_deviceid = pdev->subsystem_device; |
832 | |
833 | info("Hot Plug Subsystem Device ID: %x\n" , subsystem_deviceid); |
834 | |
835 | /* Set Vendor ID, so it can be accessed later from other |
836 | * functions |
837 | */ |
838 | ctrl->vendor_id = vendor_id; |
839 | |
840 | switch (subsystem_vid) { |
841 | case PCI_VENDOR_ID_COMPAQ: |
842 | if (pdev->revision >= 0x13) { /* CIOBX */ |
843 | ctrl->push_flag = 1; |
844 | ctrl->slot_switch_type = 1; |
845 | ctrl->push_button = 1; |
846 | ctrl->pci_config_space = 1; |
847 | ctrl->defeature_PHP = 1; |
848 | ctrl->pcix_support = 1; |
849 | ctrl->pcix_speed_capability = 1; |
850 | pci_read_config_byte(dev: pdev, where: 0x41, val: &bus_cap); |
851 | if (bus_cap & 0x80) { |
852 | dbg("bus max supports 133MHz PCI-X\n" ); |
853 | bus->max_bus_speed = PCI_SPEED_133MHz_PCIX; |
854 | break; |
855 | } |
856 | if (bus_cap & 0x40) { |
857 | dbg("bus max supports 100MHz PCI-X\n" ); |
858 | bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; |
859 | break; |
860 | } |
861 | if (bus_cap & 0x20) { |
862 | dbg("bus max supports 66MHz PCI-X\n" ); |
863 | bus->max_bus_speed = PCI_SPEED_66MHz_PCIX; |
864 | break; |
865 | } |
866 | if (bus_cap & 0x10) { |
867 | dbg("bus max supports 66MHz PCI\n" ); |
868 | bus->max_bus_speed = PCI_SPEED_66MHz; |
869 | break; |
870 | } |
871 | |
872 | break; |
873 | } |
874 | |
875 | switch (subsystem_deviceid) { |
876 | case PCI_SUB_HPC_ID: |
877 | /* Original 6500/7000 implementation */ |
878 | ctrl->slot_switch_type = 1; |
879 | bus->max_bus_speed = PCI_SPEED_33MHz; |
880 | ctrl->push_button = 0; |
881 | ctrl->pci_config_space = 1; |
882 | ctrl->defeature_PHP = 1; |
883 | ctrl->pcix_support = 0; |
884 | ctrl->pcix_speed_capability = 0; |
885 | break; |
886 | case PCI_SUB_HPC_ID2: |
887 | /* First Pushbutton implementation */ |
888 | ctrl->push_flag = 1; |
889 | ctrl->slot_switch_type = 1; |
890 | bus->max_bus_speed = PCI_SPEED_33MHz; |
891 | ctrl->push_button = 1; |
892 | ctrl->pci_config_space = 1; |
893 | ctrl->defeature_PHP = 1; |
894 | ctrl->pcix_support = 0; |
895 | ctrl->pcix_speed_capability = 0; |
896 | break; |
897 | case PCI_SUB_HPC_ID_INTC: |
898 | /* Third party (6500/7000) */ |
899 | ctrl->slot_switch_type = 1; |
900 | bus->max_bus_speed = PCI_SPEED_33MHz; |
901 | ctrl->push_button = 0; |
902 | ctrl->pci_config_space = 1; |
903 | ctrl->defeature_PHP = 1; |
904 | ctrl->pcix_support = 0; |
905 | ctrl->pcix_speed_capability = 0; |
906 | break; |
907 | case PCI_SUB_HPC_ID3: |
908 | /* First 66 Mhz implementation */ |
909 | ctrl->push_flag = 1; |
910 | ctrl->slot_switch_type = 1; |
911 | bus->max_bus_speed = PCI_SPEED_66MHz; |
912 | ctrl->push_button = 1; |
913 | ctrl->pci_config_space = 1; |
914 | ctrl->defeature_PHP = 1; |
915 | ctrl->pcix_support = 0; |
916 | ctrl->pcix_speed_capability = 0; |
917 | break; |
918 | case PCI_SUB_HPC_ID4: |
919 | /* First PCI-X implementation, 100MHz */ |
920 | ctrl->push_flag = 1; |
921 | ctrl->slot_switch_type = 1; |
922 | bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; |
923 | ctrl->push_button = 1; |
924 | ctrl->pci_config_space = 1; |
925 | ctrl->defeature_PHP = 1; |
926 | ctrl->pcix_support = 1; |
927 | ctrl->pcix_speed_capability = 0; |
928 | break; |
929 | default: |
930 | err(msg_HPC_not_supported); |
931 | rc = -ENODEV; |
932 | goto err_free_ctrl; |
933 | } |
934 | break; |
935 | |
936 | case PCI_VENDOR_ID_INTEL: |
937 | /* Check for speed capability (0=33, 1=66) */ |
938 | if (subsystem_deviceid & 0x0001) |
939 | bus->max_bus_speed = PCI_SPEED_66MHz; |
940 | else |
941 | bus->max_bus_speed = PCI_SPEED_33MHz; |
942 | |
943 | /* Check for push button */ |
944 | if (subsystem_deviceid & 0x0002) |
945 | ctrl->push_button = 0; |
946 | else |
947 | ctrl->push_button = 1; |
948 | |
949 | /* Check for slot switch type (0=mechanical, 1=not mechanical) */ |
950 | if (subsystem_deviceid & 0x0004) |
951 | ctrl->slot_switch_type = 0; |
952 | else |
953 | ctrl->slot_switch_type = 1; |
954 | |
955 | /* PHP Status (0=De-feature PHP, 1=Normal operation) */ |
956 | if (subsystem_deviceid & 0x0008) |
957 | ctrl->defeature_PHP = 1; /* PHP supported */ |
958 | else |
959 | ctrl->defeature_PHP = 0; /* PHP not supported */ |
960 | |
961 | /* Alternate Base Address Register Interface |
962 | * (0=not supported, 1=supported) |
963 | */ |
964 | if (subsystem_deviceid & 0x0010) |
965 | ctrl->alternate_base_address = 1; |
966 | else |
967 | ctrl->alternate_base_address = 0; |
968 | |
969 | /* PCI Config Space Index (0=not supported, 1=supported) */ |
970 | if (subsystem_deviceid & 0x0020) |
971 | ctrl->pci_config_space = 1; |
972 | else |
973 | ctrl->pci_config_space = 0; |
974 | |
975 | /* PCI-X support */ |
976 | if (subsystem_deviceid & 0x0080) { |
977 | ctrl->pcix_support = 1; |
978 | if (subsystem_deviceid & 0x0040) |
979 | /* 133MHz PCI-X if bit 7 is 1 */ |
980 | ctrl->pcix_speed_capability = 1; |
981 | else |
982 | /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */ |
983 | /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */ |
984 | ctrl->pcix_speed_capability = 0; |
985 | } else { |
986 | /* Conventional PCI */ |
987 | ctrl->pcix_support = 0; |
988 | ctrl->pcix_speed_capability = 0; |
989 | } |
990 | break; |
991 | |
992 | default: |
993 | err(msg_HPC_not_supported); |
994 | rc = -ENODEV; |
995 | goto err_free_ctrl; |
996 | } |
997 | |
998 | /* Tell the user that we found one. */ |
999 | info("Initializing the PCI hot plug controller residing on PCI bus %d\n" , |
1000 | pdev->bus->number); |
1001 | |
1002 | dbg("Hotplug controller capabilities:\n" ); |
1003 | dbg(" speed_capability %d\n" , bus->max_bus_speed); |
1004 | dbg(" slot_switch_type %s\n" , ctrl->slot_switch_type ? |
1005 | "switch present" : "no switch" ); |
1006 | dbg(" defeature_PHP %s\n" , ctrl->defeature_PHP ? |
1007 | "PHP supported" : "PHP not supported" ); |
1008 | dbg(" alternate_base_address %s\n" , ctrl->alternate_base_address ? |
1009 | "supported" : "not supported" ); |
1010 | dbg(" pci_config_space %s\n" , ctrl->pci_config_space ? |
1011 | "supported" : "not supported" ); |
1012 | dbg(" pcix_speed_capability %s\n" , ctrl->pcix_speed_capability ? |
1013 | "supported" : "not supported" ); |
1014 | dbg(" pcix_support %s\n" , ctrl->pcix_support ? |
1015 | "supported" : "not supported" ); |
1016 | |
1017 | ctrl->pci_dev = pdev; |
1018 | pci_set_drvdata(pdev, data: ctrl); |
1019 | |
1020 | /* make our own copy of the pci bus structure, |
1021 | * as we like tweaking it a lot */ |
1022 | ctrl->pci_bus = kmemdup(p: pdev->bus, size: sizeof(*ctrl->pci_bus), GFP_KERNEL); |
1023 | if (!ctrl->pci_bus) { |
1024 | err("out of memory\n" ); |
1025 | rc = -ENOMEM; |
1026 | goto err_free_ctrl; |
1027 | } |
1028 | |
1029 | ctrl->bus = pdev->bus->number; |
1030 | ctrl->rev = pdev->revision; |
1031 | dbg("bus device function rev: %d %d %d %d\n" , ctrl->bus, |
1032 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev); |
1033 | |
1034 | mutex_init(&ctrl->crit_sect); |
1035 | init_waitqueue_head(&ctrl->queue); |
1036 | |
1037 | /* initialize our threads if they haven't already been started up */ |
1038 | rc = one_time_init(); |
1039 | if (rc) |
1040 | goto err_free_bus; |
1041 | |
1042 | dbg("pdev = %p\n" , pdev); |
1043 | dbg("pci resource start %llx\n" , (unsigned long long)pci_resource_start(pdev, 0)); |
1044 | dbg("pci resource len %llx\n" , (unsigned long long)pci_resource_len(pdev, 0)); |
1045 | |
1046 | if (!request_mem_region(pci_resource_start(pdev, 0), |
1047 | pci_resource_len(pdev, 0), MY_NAME)) { |
1048 | err("cannot reserve MMIO region\n" ); |
1049 | rc = -ENOMEM; |
1050 | goto err_free_bus; |
1051 | } |
1052 | |
1053 | ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0), |
1054 | pci_resource_len(pdev, 0)); |
1055 | if (!ctrl->hpc_reg) { |
1056 | err("cannot remap MMIO region %llx @ %llx\n" , |
1057 | (unsigned long long)pci_resource_len(pdev, 0), |
1058 | (unsigned long long)pci_resource_start(pdev, 0)); |
1059 | rc = -ENODEV; |
1060 | goto err_free_mem_region; |
1061 | } |
1062 | |
1063 | /* Check for 66Mhz operation */ |
1064 | bus->cur_bus_speed = get_controller_speed(ctrl); |
1065 | |
1066 | |
1067 | /******************************************************** |
1068 | * |
1069 | * Save configuration headers for this and |
1070 | * subordinate PCI buses |
1071 | * |
1072 | ********************************************************/ |
1073 | |
1074 | /* find the physical slot number of the first hot plug slot */ |
1075 | |
1076 | /* Get slot won't work for devices behind bridges, but |
1077 | * in this case it will always be called for the "base" |
1078 | * bus/dev/func of a slot. |
1079 | * CS: this is leveraging the PCIIRQ routing code from the kernel |
1080 | * (pci-pc.c: get_irq_routing_table) */ |
1081 | rc = get_slot_mapping(bus: ctrl->pci_bus, bus_num: pdev->bus->number, |
1082 | dev_num: (readb(addr: ctrl->hpc_reg + SLOT_MASK) >> 4), |
1083 | slot: &(ctrl->first_slot)); |
1084 | dbg("get_slot_mapping: first_slot = %d, returned = %d\n" , |
1085 | ctrl->first_slot, rc); |
1086 | if (rc) { |
1087 | err(msg_initialization_err, rc); |
1088 | goto err_iounmap; |
1089 | } |
1090 | |
1091 | /* Store PCI Config Space for all devices on this bus */ |
1092 | rc = cpqhp_save_config(ctrl, busnumber: ctrl->bus, readb(addr: ctrl->hpc_reg + SLOT_MASK)); |
1093 | if (rc) { |
1094 | err("%s: unable to save PCI configuration data, error %d\n" , |
1095 | __func__, rc); |
1096 | goto err_iounmap; |
1097 | } |
1098 | |
1099 | /* |
1100 | * Get IO, memory, and IRQ resources for new devices |
1101 | */ |
1102 | /* The next line is required for cpqhp_find_available_resources */ |
1103 | ctrl->interrupt = pdev->irq; |
1104 | if (ctrl->interrupt < 0x10) { |
1105 | cpqhp_legacy_mode = 1; |
1106 | dbg("System seems to be configured for Full Table Mapped MPS mode\n" ); |
1107 | } |
1108 | |
1109 | ctrl->cfgspc_irq = 0; |
1110 | pci_read_config_byte(dev: pdev, PCI_INTERRUPT_LINE, val: &ctrl->cfgspc_irq); |
1111 | |
1112 | rc = cpqhp_find_available_resources(ctrl, rom_start: cpqhp_rom_start); |
1113 | ctrl->add_support = !rc; |
1114 | if (rc) { |
1115 | dbg("cpqhp_find_available_resources = 0x%x\n" , rc); |
1116 | err("unable to locate PCI configuration resources for hot plug add.\n" ); |
1117 | goto err_iounmap; |
1118 | } |
1119 | |
1120 | /* |
1121 | * Finish setting up the hot plug ctrl device |
1122 | */ |
1123 | ctrl->slot_device_offset = readb(addr: ctrl->hpc_reg + SLOT_MASK) >> 4; |
1124 | dbg("NumSlots %d\n" , ctrl->slot_device_offset); |
1125 | |
1126 | ctrl->next_event = 0; |
1127 | |
1128 | /* Setup the slot information structures */ |
1129 | rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table); |
1130 | if (rc) { |
1131 | err(msg_initialization_err, 6); |
1132 | err("%s: unable to save PCI configuration data, error %d\n" , |
1133 | __func__, rc); |
1134 | goto err_iounmap; |
1135 | } |
1136 | |
1137 | /* Mask all general input interrupts */ |
1138 | writel(val: 0xFFFFFFFFL, addr: ctrl->hpc_reg + INT_MASK); |
1139 | |
1140 | /* set up the interrupt */ |
1141 | dbg("HPC interrupt = %d\n" , ctrl->interrupt); |
1142 | if (request_irq(irq: ctrl->interrupt, handler: cpqhp_ctrl_intr, |
1143 | IRQF_SHARED, MY_NAME, dev: ctrl)) { |
1144 | err("Can't get irq %d for the hotplug pci controller\n" , |
1145 | ctrl->interrupt); |
1146 | rc = -ENODEV; |
1147 | goto err_iounmap; |
1148 | } |
1149 | |
1150 | /* Enable Shift Out interrupt and clear it, also enable SERR on power |
1151 | * fault |
1152 | */ |
1153 | temp_word = readw(addr: ctrl->hpc_reg + MISC); |
1154 | temp_word |= 0x4006; |
1155 | writew(val: temp_word, addr: ctrl->hpc_reg + MISC); |
1156 | |
1157 | /* Changed 05/05/97 to clear all interrupts at start */ |
1158 | writel(val: 0xFFFFFFFFL, addr: ctrl->hpc_reg + INT_INPUT_CLEAR); |
1159 | |
1160 | ctrl->ctrl_int_comp = readl(addr: ctrl->hpc_reg + INT_INPUT_CLEAR); |
1161 | |
1162 | writel(val: 0x0L, addr: ctrl->hpc_reg + INT_MASK); |
1163 | |
1164 | if (!cpqhp_ctrl_list) { |
1165 | cpqhp_ctrl_list = ctrl; |
1166 | ctrl->next = NULL; |
1167 | } else { |
1168 | ctrl->next = cpqhp_ctrl_list; |
1169 | cpqhp_ctrl_list = ctrl; |
1170 | } |
1171 | |
1172 | /* turn off empty slots here unless command line option "ON" set |
1173 | * Wait for exclusive access to hardware |
1174 | */ |
1175 | mutex_lock(&ctrl->crit_sect); |
1176 | |
1177 | num_of_slots = readb(addr: ctrl->hpc_reg + SLOT_MASK) & 0x0F; |
1178 | |
1179 | /* find first device number for the ctrl */ |
1180 | device = readb(addr: ctrl->hpc_reg + SLOT_MASK) >> 4; |
1181 | |
1182 | while (num_of_slots) { |
1183 | dbg("num_of_slots: %d\n" , num_of_slots); |
1184 | func = cpqhp_slot_find(bus: ctrl->bus, device, index: 0); |
1185 | if (!func) |
1186 | break; |
1187 | |
1188 | hp_slot = func->device - ctrl->slot_device_offset; |
1189 | dbg("hp_slot: %d\n" , hp_slot); |
1190 | |
1191 | /* We have to save the presence info for these slots */ |
1192 | temp_word = ctrl->ctrl_int_comp >> 16; |
1193 | func->presence_save = (temp_word >> hp_slot) & 0x01; |
1194 | func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; |
1195 | |
1196 | if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) |
1197 | func->switch_save = 0; |
1198 | else |
1199 | func->switch_save = 0x10; |
1200 | |
1201 | if (!power_mode) |
1202 | if (!func->is_a_board) { |
1203 | green_LED_off(ctrl, slot: hp_slot); |
1204 | slot_disable(ctrl, slot: hp_slot); |
1205 | } |
1206 | |
1207 | device++; |
1208 | num_of_slots--; |
1209 | } |
1210 | |
1211 | if (!power_mode) { |
1212 | set_SOGO(ctrl); |
1213 | /* Wait for SOBS to be unset */ |
1214 | wait_for_ctrl_irq(ctrl); |
1215 | } |
1216 | |
1217 | rc = init_SERR(ctrl); |
1218 | if (rc) { |
1219 | err("init_SERR failed\n" ); |
1220 | mutex_unlock(lock: &ctrl->crit_sect); |
1221 | goto err_free_irq; |
1222 | } |
1223 | |
1224 | /* Done with exclusive hardware access */ |
1225 | mutex_unlock(lock: &ctrl->crit_sect); |
1226 | |
1227 | cpqhp_create_debugfs_files(ctrl); |
1228 | |
1229 | return 0; |
1230 | |
1231 | err_free_irq: |
1232 | free_irq(ctrl->interrupt, ctrl); |
1233 | err_iounmap: |
1234 | iounmap(addr: ctrl->hpc_reg); |
1235 | err_free_mem_region: |
1236 | release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); |
1237 | err_free_bus: |
1238 | kfree(objp: ctrl->pci_bus); |
1239 | err_free_ctrl: |
1240 | kfree(objp: ctrl); |
1241 | err_disable_device: |
1242 | pci_disable_device(dev: pdev); |
1243 | return rc; |
1244 | } |
1245 | |
1246 | static void __exit unload_cpqphpd(void) |
1247 | { |
1248 | struct pci_func *next; |
1249 | struct pci_func *TempSlot; |
1250 | int loop; |
1251 | u32 rc; |
1252 | struct controller *ctrl; |
1253 | struct controller *tctrl; |
1254 | struct pci_resource *res; |
1255 | struct pci_resource *tres; |
1256 | |
1257 | compaq_nvram_store(rom_start: cpqhp_rom_start); |
1258 | |
1259 | ctrl = cpqhp_ctrl_list; |
1260 | |
1261 | while (ctrl) { |
1262 | if (ctrl->hpc_reg) { |
1263 | u16 misc; |
1264 | rc = read_slot_enable(ctrl); |
1265 | |
1266 | writeb(val: 0, addr: ctrl->hpc_reg + SLOT_SERR); |
1267 | writel(val: 0xFFFFFFC0L | ~rc, addr: ctrl->hpc_reg + INT_MASK); |
1268 | |
1269 | misc = readw(addr: ctrl->hpc_reg + MISC); |
1270 | misc &= 0xFFFD; |
1271 | writew(val: misc, addr: ctrl->hpc_reg + MISC); |
1272 | } |
1273 | |
1274 | ctrl_slot_cleanup(ctrl); |
1275 | |
1276 | res = ctrl->io_head; |
1277 | while (res) { |
1278 | tres = res; |
1279 | res = res->next; |
1280 | kfree(objp: tres); |
1281 | } |
1282 | |
1283 | res = ctrl->mem_head; |
1284 | while (res) { |
1285 | tres = res; |
1286 | res = res->next; |
1287 | kfree(objp: tres); |
1288 | } |
1289 | |
1290 | res = ctrl->p_mem_head; |
1291 | while (res) { |
1292 | tres = res; |
1293 | res = res->next; |
1294 | kfree(objp: tres); |
1295 | } |
1296 | |
1297 | res = ctrl->bus_head; |
1298 | while (res) { |
1299 | tres = res; |
1300 | res = res->next; |
1301 | kfree(objp: tres); |
1302 | } |
1303 | |
1304 | kfree(objp: ctrl->pci_bus); |
1305 | |
1306 | tctrl = ctrl; |
1307 | ctrl = ctrl->next; |
1308 | kfree(objp: tctrl); |
1309 | } |
1310 | |
1311 | for (loop = 0; loop < 256; loop++) { |
1312 | next = cpqhp_slot_list[loop]; |
1313 | while (next != NULL) { |
1314 | res = next->io_head; |
1315 | while (res) { |
1316 | tres = res; |
1317 | res = res->next; |
1318 | kfree(objp: tres); |
1319 | } |
1320 | |
1321 | res = next->mem_head; |
1322 | while (res) { |
1323 | tres = res; |
1324 | res = res->next; |
1325 | kfree(objp: tres); |
1326 | } |
1327 | |
1328 | res = next->p_mem_head; |
1329 | while (res) { |
1330 | tres = res; |
1331 | res = res->next; |
1332 | kfree(objp: tres); |
1333 | } |
1334 | |
1335 | res = next->bus_head; |
1336 | while (res) { |
1337 | tres = res; |
1338 | res = res->next; |
1339 | kfree(objp: tres); |
1340 | } |
1341 | |
1342 | TempSlot = next; |
1343 | next = next->next; |
1344 | kfree(objp: TempSlot); |
1345 | } |
1346 | } |
1347 | |
1348 | /* Stop the notification mechanism */ |
1349 | if (initialized) |
1350 | cpqhp_event_stop_thread(); |
1351 | |
1352 | /* unmap the rom address */ |
1353 | if (cpqhp_rom_start) |
1354 | iounmap(addr: cpqhp_rom_start); |
1355 | if (smbios_start) |
1356 | iounmap(addr: smbios_start); |
1357 | } |
1358 | |
1359 | static const struct pci_device_id hpcd_pci_tbl[] = { |
1360 | { |
1361 | /* handle any PCI Hotplug controller */ |
1362 | .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), |
1363 | .class_mask = ~0, |
1364 | |
1365 | /* no matter who makes it */ |
1366 | .vendor = PCI_ANY_ID, |
1367 | .device = PCI_ANY_ID, |
1368 | .subvendor = PCI_ANY_ID, |
1369 | .subdevice = PCI_ANY_ID, |
1370 | |
1371 | }, { /* end: all zeroes */ } |
1372 | }; |
1373 | |
1374 | MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl); |
1375 | |
1376 | static struct pci_driver cpqhpc_driver = { |
1377 | .name = "compaq_pci_hotplug" , |
1378 | .id_table = hpcd_pci_tbl, |
1379 | .probe = cpqhpc_probe, |
1380 | /* remove: cpqhpc_remove_one, */ |
1381 | }; |
1382 | |
1383 | static int __init cpqhpc_init(void) |
1384 | { |
1385 | int result; |
1386 | |
1387 | cpqhp_debug = debug; |
1388 | |
1389 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n" ); |
1390 | cpqhp_initialize_debugfs(); |
1391 | result = pci_register_driver(&cpqhpc_driver); |
1392 | dbg("pci_register_driver = %d\n" , result); |
1393 | return result; |
1394 | } |
1395 | |
1396 | static void __exit cpqhpc_cleanup(void) |
1397 | { |
1398 | dbg("unload_cpqphpd()\n" ); |
1399 | unload_cpqphpd(); |
1400 | |
1401 | dbg("pci_unregister_driver\n" ); |
1402 | pci_unregister_driver(dev: &cpqhpc_driver); |
1403 | cpqhp_shutdown_debugfs(); |
1404 | } |
1405 | |
1406 | module_init(cpqhpc_init); |
1407 | module_exit(cpqhpc_cleanup); |
1408 | |