1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * IBM Hot Plug Controller Driver |
4 | * |
5 | * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation |
6 | * |
7 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) |
8 | * Copyright (C) 2001-2003 IBM Corp. |
9 | * |
10 | * All rights reserved. |
11 | * |
12 | * Send feedback to <gregkh@us.ibm.com> |
13 | * |
14 | */ |
15 | |
16 | #include <linux/init.h> |
17 | #include <linux/module.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/pci.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/delay.h> |
22 | #include <linux/wait.h> |
23 | #include "../pci.h" |
24 | #include <asm/pci_x86.h> /* for struct irq_routing_table */ |
25 | #include <asm/io_apic.h> |
26 | #include "ibmphp.h" |
27 | |
28 | #define attn_on(sl) ibmphp_hpc_writeslot(sl, HPC_SLOT_ATTNON) |
29 | #define attn_off(sl) ibmphp_hpc_writeslot(sl, HPC_SLOT_ATTNOFF) |
30 | #define attn_LED_blink(sl) ibmphp_hpc_writeslot(sl, HPC_SLOT_BLINKLED) |
31 | #define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot(sl, READ_REVLEVEL, rev) |
32 | #define get_hpc_options(sl, opt) ibmphp_hpc_readslot(sl, READ_HPCOPTIONS, opt) |
33 | |
34 | #define DRIVER_VERSION "0.6" |
35 | #define DRIVER_DESC "IBM Hot Plug PCI Controller Driver" |
36 | |
37 | int ibmphp_debug; |
38 | |
39 | static bool debug; |
40 | module_param(debug, bool, S_IRUGO | S_IWUSR); |
41 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not" ); |
42 | MODULE_LICENSE("GPL" ); |
43 | MODULE_DESCRIPTION(DRIVER_DESC); |
44 | |
45 | struct pci_bus *ibmphp_pci_bus; |
46 | static int max_slots; |
47 | |
48 | static int irqs[16]; /* PIC mode IRQs we're using so far (in case MPS |
49 | * tables don't provide default info for empty slots */ |
50 | |
51 | static int init_flag; |
52 | |
53 | static inline int get_cur_bus_info(struct slot **sl) |
54 | { |
55 | int rc = 1; |
56 | struct slot *slot_cur = *sl; |
57 | |
58 | debug("options = %x\n" , slot_cur->ctrl->options); |
59 | debug("revision = %x\n" , slot_cur->ctrl->revision); |
60 | |
61 | if (READ_BUS_STATUS(slot_cur->ctrl)) |
62 | rc = ibmphp_hpc_readslot(slot_cur, READ_BUSSTATUS, NULL); |
63 | |
64 | if (rc) |
65 | return rc; |
66 | |
67 | slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED(slot_cur->busstatus); |
68 | if (READ_BUS_MODE(slot_cur->ctrl)) |
69 | slot_cur->bus_on->current_bus_mode = |
70 | CURRENT_BUS_MODE(slot_cur->busstatus); |
71 | else |
72 | slot_cur->bus_on->current_bus_mode = 0xFF; |
73 | |
74 | debug("busstatus = %x, bus_speed = %x, bus_mode = %x\n" , |
75 | slot_cur->busstatus, |
76 | slot_cur->bus_on->current_speed, |
77 | slot_cur->bus_on->current_bus_mode); |
78 | |
79 | *sl = slot_cur; |
80 | return 0; |
81 | } |
82 | |
83 | static inline int slot_update(struct slot **sl) |
84 | { |
85 | int rc; |
86 | rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL); |
87 | if (rc) |
88 | return rc; |
89 | if (!init_flag) |
90 | rc = get_cur_bus_info(sl); |
91 | return rc; |
92 | } |
93 | |
94 | static int __init get_max_slots(void) |
95 | { |
96 | struct slot *slot_cur; |
97 | u8 slot_count = 0; |
98 | |
99 | list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) { |
100 | /* sometimes the hot-pluggable slots start with 4 (not always from 1) */ |
101 | slot_count = max(slot_count, slot_cur->number); |
102 | } |
103 | return slot_count; |
104 | } |
105 | |
106 | /* This routine will put the correct slot->device information per slot. It's |
107 | * called from initialization of the slot structures. It will also assign |
108 | * interrupt numbers per each slot. |
109 | * Parameters: struct slot |
110 | * Returns 0 or errors |
111 | */ |
112 | int ibmphp_init_devno(struct slot **cur_slot) |
113 | { |
114 | struct irq_routing_table *rtable; |
115 | int len; |
116 | int loop; |
117 | int i; |
118 | |
119 | rtable = pcibios_get_irq_routing_table(); |
120 | if (!rtable) { |
121 | err("no BIOS routing table...\n" ); |
122 | return -ENOMEM; |
123 | } |
124 | |
125 | len = (rtable->size - sizeof(struct irq_routing_table)) / |
126 | sizeof(struct irq_info); |
127 | |
128 | if (!len) { |
129 | kfree(objp: rtable); |
130 | return -1; |
131 | } |
132 | for (loop = 0; loop < len; loop++) { |
133 | if ((*cur_slot)->number == rtable->slots[loop].slot && |
134 | (*cur_slot)->bus == rtable->slots[loop].bus) { |
135 | (*cur_slot)->device = PCI_SLOT(rtable->slots[loop].devfn); |
136 | for (i = 0; i < 4; i++) |
137 | (*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector(bus: (int) (*cur_slot)->bus, |
138 | devfn: (int) (*cur_slot)->device, pin: i); |
139 | |
140 | debug("(*cur_slot)->irq[0] = %x\n" , |
141 | (*cur_slot)->irq[0]); |
142 | debug("(*cur_slot)->irq[1] = %x\n" , |
143 | (*cur_slot)->irq[1]); |
144 | debug("(*cur_slot)->irq[2] = %x\n" , |
145 | (*cur_slot)->irq[2]); |
146 | debug("(*cur_slot)->irq[3] = %x\n" , |
147 | (*cur_slot)->irq[3]); |
148 | |
149 | debug("rtable->exclusive_irqs = %x\n" , |
150 | rtable->exclusive_irqs); |
151 | debug("rtable->slots[loop].irq[0].bitmap = %x\n" , |
152 | rtable->slots[loop].irq[0].bitmap); |
153 | debug("rtable->slots[loop].irq[1].bitmap = %x\n" , |
154 | rtable->slots[loop].irq[1].bitmap); |
155 | debug("rtable->slots[loop].irq[2].bitmap = %x\n" , |
156 | rtable->slots[loop].irq[2].bitmap); |
157 | debug("rtable->slots[loop].irq[3].bitmap = %x\n" , |
158 | rtable->slots[loop].irq[3].bitmap); |
159 | |
160 | debug("rtable->slots[loop].irq[0].link = %x\n" , |
161 | rtable->slots[loop].irq[0].link); |
162 | debug("rtable->slots[loop].irq[1].link = %x\n" , |
163 | rtable->slots[loop].irq[1].link); |
164 | debug("rtable->slots[loop].irq[2].link = %x\n" , |
165 | rtable->slots[loop].irq[2].link); |
166 | debug("rtable->slots[loop].irq[3].link = %x\n" , |
167 | rtable->slots[loop].irq[3].link); |
168 | debug("end of init_devno\n" ); |
169 | kfree(objp: rtable); |
170 | return 0; |
171 | } |
172 | } |
173 | |
174 | kfree(objp: rtable); |
175 | return -1; |
176 | } |
177 | |
178 | static inline int power_on(struct slot *slot_cur) |
179 | { |
180 | u8 cmd = HPC_SLOT_ON; |
181 | int retval; |
182 | |
183 | retval = ibmphp_hpc_writeslot(slot_cur, cmd); |
184 | if (retval) { |
185 | err("power on failed\n" ); |
186 | return retval; |
187 | } |
188 | if (CTLR_RESULT(slot_cur->ctrl->status)) { |
189 | err("command not completed successfully in power_on\n" ); |
190 | return -EIO; |
191 | } |
192 | msleep(msecs: 3000); /* For ServeRAID cards, and some 66 PCI */ |
193 | return 0; |
194 | } |
195 | |
196 | static inline int power_off(struct slot *slot_cur) |
197 | { |
198 | u8 cmd = HPC_SLOT_OFF; |
199 | int retval; |
200 | |
201 | retval = ibmphp_hpc_writeslot(slot_cur, cmd); |
202 | if (retval) { |
203 | err("power off failed\n" ); |
204 | return retval; |
205 | } |
206 | if (CTLR_RESULT(slot_cur->ctrl->status)) { |
207 | err("command not completed successfully in power_off\n" ); |
208 | retval = -EIO; |
209 | } |
210 | return retval; |
211 | } |
212 | |
213 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) |
214 | { |
215 | int rc = 0; |
216 | struct slot *pslot; |
217 | u8 cmd = 0x00; /* avoid compiler warning */ |
218 | |
219 | debug("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n" , |
220 | (ulong) hotplug_slot, value); |
221 | ibmphp_lock_operations(); |
222 | |
223 | |
224 | if (hotplug_slot) { |
225 | switch (value) { |
226 | case HPC_SLOT_ATTN_OFF: |
227 | cmd = HPC_SLOT_ATTNOFF; |
228 | break; |
229 | case HPC_SLOT_ATTN_ON: |
230 | cmd = HPC_SLOT_ATTNON; |
231 | break; |
232 | case HPC_SLOT_ATTN_BLINK: |
233 | cmd = HPC_SLOT_BLINKLED; |
234 | break; |
235 | default: |
236 | rc = -ENODEV; |
237 | err("set_attention_status - Error : invalid input [%x]\n" , |
238 | value); |
239 | break; |
240 | } |
241 | if (rc == 0) { |
242 | pslot = to_slot(hotplug_slot); |
243 | rc = ibmphp_hpc_writeslot(pslot, cmd); |
244 | } |
245 | } else |
246 | rc = -ENODEV; |
247 | |
248 | ibmphp_unlock_operations(); |
249 | |
250 | debug("set_attention_status - Exit rc[%d]\n" , rc); |
251 | return rc; |
252 | } |
253 | |
254 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) |
255 | { |
256 | int rc = -ENODEV; |
257 | struct slot *pslot; |
258 | struct slot myslot; |
259 | |
260 | debug("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n" , |
261 | (ulong) hotplug_slot, (ulong) value); |
262 | |
263 | ibmphp_lock_operations(); |
264 | if (hotplug_slot) { |
265 | pslot = to_slot(hotplug_slot); |
266 | memcpy(&myslot, pslot, sizeof(struct slot)); |
267 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, |
268 | &myslot.status); |
269 | if (!rc) |
270 | rc = ibmphp_hpc_readslot(pslot, READ_EXTSLOTSTATUS, |
271 | &myslot.ext_status); |
272 | if (!rc) |
273 | *value = SLOT_ATTN(myslot.status, myslot.ext_status); |
274 | } |
275 | |
276 | ibmphp_unlock_operations(); |
277 | debug("get_attention_status - Exit rc[%d] value[%x]\n" , rc, *value); |
278 | return rc; |
279 | } |
280 | |
281 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) |
282 | { |
283 | int rc = -ENODEV; |
284 | struct slot *pslot; |
285 | struct slot myslot; |
286 | |
287 | debug("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n" , |
288 | (ulong) hotplug_slot, (ulong) value); |
289 | ibmphp_lock_operations(); |
290 | if (hotplug_slot) { |
291 | pslot = to_slot(hotplug_slot); |
292 | memcpy(&myslot, pslot, sizeof(struct slot)); |
293 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, |
294 | &myslot.status); |
295 | if (!rc) |
296 | *value = SLOT_LATCH(myslot.status); |
297 | } |
298 | |
299 | ibmphp_unlock_operations(); |
300 | debug("get_latch_status - Exit rc[%d] rc[%x] value[%x]\n" , |
301 | rc, rc, *value); |
302 | return rc; |
303 | } |
304 | |
305 | |
306 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) |
307 | { |
308 | int rc = -ENODEV; |
309 | struct slot *pslot; |
310 | struct slot myslot; |
311 | |
312 | debug("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n" , |
313 | (ulong) hotplug_slot, (ulong) value); |
314 | ibmphp_lock_operations(); |
315 | if (hotplug_slot) { |
316 | pslot = to_slot(hotplug_slot); |
317 | memcpy(&myslot, pslot, sizeof(struct slot)); |
318 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, |
319 | &myslot.status); |
320 | if (!rc) |
321 | *value = SLOT_PWRGD(myslot.status); |
322 | } |
323 | |
324 | ibmphp_unlock_operations(); |
325 | debug("get_power_status - Exit rc[%d] rc[%x] value[%x]\n" , |
326 | rc, rc, *value); |
327 | return rc; |
328 | } |
329 | |
330 | static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value) |
331 | { |
332 | int rc = -ENODEV; |
333 | struct slot *pslot; |
334 | u8 present; |
335 | struct slot myslot; |
336 | |
337 | debug("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n" , |
338 | (ulong) hotplug_slot, (ulong) value); |
339 | ibmphp_lock_operations(); |
340 | if (hotplug_slot) { |
341 | pslot = to_slot(hotplug_slot); |
342 | memcpy(&myslot, pslot, sizeof(struct slot)); |
343 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, |
344 | &myslot.status); |
345 | if (!rc) { |
346 | present = SLOT_PRESENT(myslot.status); |
347 | if (present == HPC_SLOT_EMPTY) |
348 | *value = 0; |
349 | else |
350 | *value = 1; |
351 | } |
352 | } |
353 | |
354 | ibmphp_unlock_operations(); |
355 | debug("get_adapter_present - Exit rc[%d] value[%x]\n" , rc, *value); |
356 | return rc; |
357 | } |
358 | |
359 | static int get_max_bus_speed(struct slot *slot) |
360 | { |
361 | int rc = 0; |
362 | u8 mode = 0; |
363 | enum pci_bus_speed speed; |
364 | struct pci_bus *bus = slot->hotplug_slot.pci_slot->bus; |
365 | |
366 | debug("%s - Entry slot[%p]\n" , __func__, slot); |
367 | |
368 | ibmphp_lock_operations(); |
369 | mode = slot->supported_bus_mode; |
370 | speed = slot->supported_speed; |
371 | ibmphp_unlock_operations(); |
372 | |
373 | switch (speed) { |
374 | case BUS_SPEED_33: |
375 | break; |
376 | case BUS_SPEED_66: |
377 | if (mode == BUS_MODE_PCIX) |
378 | speed += 0x01; |
379 | break; |
380 | case BUS_SPEED_100: |
381 | case BUS_SPEED_133: |
382 | speed += 0x01; |
383 | break; |
384 | default: |
385 | /* Note (will need to change): there would be soon 256, 512 also */ |
386 | rc = -ENODEV; |
387 | } |
388 | |
389 | if (!rc) |
390 | bus->max_bus_speed = speed; |
391 | |
392 | debug("%s - Exit rc[%d] speed[%x]\n" , __func__, rc, speed); |
393 | return rc; |
394 | } |
395 | |
396 | /**************************************************************************** |
397 | * This routine will initialize the ops data structure used in the validate |
398 | * function. It will also power off empty slots that are powered on since BIOS |
399 | * leaves those on, albeit disconnected |
400 | ****************************************************************************/ |
401 | static int __init init_ops(void) |
402 | { |
403 | struct slot *slot_cur; |
404 | int retval; |
405 | int rc; |
406 | |
407 | list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) { |
408 | debug("BEFORE GETTING SLOT STATUS, slot # %x\n" , |
409 | slot_cur->number); |
410 | if (slot_cur->ctrl->revision == 0xFF) |
411 | if (get_ctrl_revision(slot_cur, |
412 | &slot_cur->ctrl->revision)) |
413 | return -1; |
414 | |
415 | if (slot_cur->bus_on->current_speed == 0xFF) |
416 | if (get_cur_bus_info(sl: &slot_cur)) |
417 | return -1; |
418 | get_max_bus_speed(slot: slot_cur); |
419 | |
420 | if (slot_cur->ctrl->options == 0xFF) |
421 | if (get_hpc_options(slot_cur, &slot_cur->ctrl->options)) |
422 | return -1; |
423 | |
424 | retval = slot_update(sl: &slot_cur); |
425 | if (retval) |
426 | return retval; |
427 | |
428 | debug("status = %x\n" , slot_cur->status); |
429 | debug("ext_status = %x\n" , slot_cur->ext_status); |
430 | debug("SLOT_POWER = %x\n" , SLOT_POWER(slot_cur->status)); |
431 | debug("SLOT_PRESENT = %x\n" , SLOT_PRESENT(slot_cur->status)); |
432 | debug("SLOT_LATCH = %x\n" , SLOT_LATCH(slot_cur->status)); |
433 | |
434 | if ((SLOT_PWRGD(slot_cur->status)) && |
435 | !(SLOT_PRESENT(slot_cur->status)) && |
436 | !(SLOT_LATCH(slot_cur->status))) { |
437 | debug("BEFORE POWER OFF COMMAND\n" ); |
438 | rc = power_off(slot_cur); |
439 | if (rc) |
440 | return rc; |
441 | |
442 | /* retval = slot_update(&slot_cur); |
443 | * if (retval) |
444 | * return retval; |
445 | * ibmphp_update_slot_info(slot_cur); |
446 | */ |
447 | } |
448 | } |
449 | init_flag = 0; |
450 | return 0; |
451 | } |
452 | |
453 | /* This operation will check whether the slot is within the bounds and |
454 | * the operation is valid to perform on that slot |
455 | * Parameters: slot, operation |
456 | * Returns: 0 or error codes |
457 | */ |
458 | static int validate(struct slot *slot_cur, int opn) |
459 | { |
460 | int number; |
461 | int retval; |
462 | |
463 | if (!slot_cur) |
464 | return -ENODEV; |
465 | number = slot_cur->number; |
466 | if ((number > max_slots) || (number < 0)) |
467 | return -EBADSLT; |
468 | debug("slot_number in validate is %d\n" , slot_cur->number); |
469 | |
470 | retval = slot_update(sl: &slot_cur); |
471 | if (retval) |
472 | return retval; |
473 | |
474 | switch (opn) { |
475 | case ENABLE: |
476 | if (!(SLOT_PWRGD(slot_cur->status)) && |
477 | (SLOT_PRESENT(slot_cur->status)) && |
478 | !(SLOT_LATCH(slot_cur->status))) |
479 | return 0; |
480 | break; |
481 | case DISABLE: |
482 | if ((SLOT_PWRGD(slot_cur->status)) && |
483 | (SLOT_PRESENT(slot_cur->status)) && |
484 | !(SLOT_LATCH(slot_cur->status))) |
485 | return 0; |
486 | break; |
487 | default: |
488 | break; |
489 | } |
490 | err("validate failed....\n" ); |
491 | return -EINVAL; |
492 | } |
493 | |
494 | /**************************************************************************** |
495 | * This routine is for updating the data structures in the hotplug core |
496 | * Parameters: struct slot |
497 | * Returns: 0 or error |
498 | ****************************************************************************/ |
499 | int ibmphp_update_slot_info(struct slot *slot_cur) |
500 | { |
501 | struct pci_bus *bus = slot_cur->hotplug_slot.pci_slot->bus; |
502 | u8 bus_speed; |
503 | u8 mode; |
504 | |
505 | bus_speed = slot_cur->bus_on->current_speed; |
506 | mode = slot_cur->bus_on->current_bus_mode; |
507 | |
508 | switch (bus_speed) { |
509 | case BUS_SPEED_33: |
510 | break; |
511 | case BUS_SPEED_66: |
512 | if (mode == BUS_MODE_PCIX) |
513 | bus_speed += 0x01; |
514 | else if (mode == BUS_MODE_PCI) |
515 | ; |
516 | else |
517 | bus_speed = PCI_SPEED_UNKNOWN; |
518 | break; |
519 | case BUS_SPEED_100: |
520 | case BUS_SPEED_133: |
521 | bus_speed += 0x01; |
522 | break; |
523 | default: |
524 | bus_speed = PCI_SPEED_UNKNOWN; |
525 | } |
526 | |
527 | bus->cur_bus_speed = bus_speed; |
528 | // To do: bus_names |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | |
534 | /****************************************************************************** |
535 | * This function will return the pci_func, given bus and devfunc, or NULL. It |
536 | * is called from visit routines |
537 | ******************************************************************************/ |
538 | |
539 | static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function) |
540 | { |
541 | struct pci_func *func_cur; |
542 | struct slot *slot_cur; |
543 | list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) { |
544 | if (slot_cur->func) { |
545 | func_cur = slot_cur->func; |
546 | while (func_cur) { |
547 | if ((func_cur->busno == busno) && |
548 | (func_cur->device == device) && |
549 | (func_cur->function == function)) |
550 | return func_cur; |
551 | func_cur = func_cur->next; |
552 | } |
553 | } |
554 | } |
555 | return NULL; |
556 | } |
557 | |
558 | /************************************************************* |
559 | * This routine frees up memory used by struct slot, including |
560 | * the pointers to pci_func, bus, hotplug_slot, controller, |
561 | * and deregistering from the hotplug core |
562 | *************************************************************/ |
563 | static void free_slots(void) |
564 | { |
565 | struct slot *slot_cur, *next; |
566 | |
567 | debug("%s -- enter\n" , __func__); |
568 | |
569 | list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head, |
570 | ibm_slot_list) { |
571 | pci_hp_del(slot: &slot_cur->hotplug_slot); |
572 | slot_cur->ctrl = NULL; |
573 | slot_cur->bus_on = NULL; |
574 | |
575 | /* |
576 | * We don't want to actually remove the resources, |
577 | * since ibmphp_free_resources() will do just that. |
578 | */ |
579 | ibmphp_unconfigure_card(&slot_cur, -1); |
580 | |
581 | pci_hp_destroy(slot: &slot_cur->hotplug_slot); |
582 | kfree(objp: slot_cur); |
583 | } |
584 | debug("%s -- exit\n" , __func__); |
585 | } |
586 | |
587 | static void ibm_unconfigure_device(struct pci_func *func) |
588 | { |
589 | struct pci_dev *temp; |
590 | u8 j; |
591 | |
592 | debug("inside %s\n" , __func__); |
593 | debug("func->device = %x, func->function = %x\n" , |
594 | func->device, func->function); |
595 | debug("func->device << 3 | 0x0 = %x\n" , func->device << 3 | 0x0); |
596 | |
597 | pci_lock_rescan_remove(); |
598 | |
599 | for (j = 0; j < 0x08; j++) { |
600 | temp = pci_get_domain_bus_and_slot(domain: 0, bus: func->busno, |
601 | devfn: (func->device << 3) | j); |
602 | if (temp) { |
603 | pci_stop_and_remove_bus_device(dev: temp); |
604 | pci_dev_put(dev: temp); |
605 | } |
606 | } |
607 | |
608 | pci_dev_put(dev: func->dev); |
609 | |
610 | pci_unlock_rescan_remove(); |
611 | } |
612 | |
613 | /* |
614 | * The following function is to fix kernel bug regarding |
615 | * getting bus entries, here we manually add those primary |
616 | * bus entries to kernel bus structure whenever apply |
617 | */ |
618 | static u8 bus_structure_fixup(u8 busno) |
619 | { |
620 | struct pci_bus *bus, *b; |
621 | struct pci_dev *dev; |
622 | u16 l; |
623 | |
624 | if (pci_find_bus(domain: 0, busnr: busno) || !(ibmphp_find_same_bus_num(busno))) |
625 | return 1; |
626 | |
627 | bus = kmalloc(size: sizeof(*bus), GFP_KERNEL); |
628 | if (!bus) |
629 | return 1; |
630 | |
631 | dev = kmalloc(size: sizeof(*dev), GFP_KERNEL); |
632 | if (!dev) { |
633 | kfree(objp: bus); |
634 | return 1; |
635 | } |
636 | |
637 | bus->number = busno; |
638 | bus->ops = ibmphp_pci_bus->ops; |
639 | dev->bus = bus; |
640 | for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) { |
641 | if (!pci_read_config_word(dev, PCI_VENDOR_ID, val: &l) && |
642 | (l != 0x0000) && (l != 0xffff)) { |
643 | debug("%s - Inside bus_structure_fixup()\n" , |
644 | __func__); |
645 | b = pci_scan_bus(bus: busno, ops: ibmphp_pci_bus->ops, NULL); |
646 | if (!b) |
647 | continue; |
648 | |
649 | pci_bus_add_devices(bus: b); |
650 | break; |
651 | } |
652 | } |
653 | |
654 | kfree(objp: dev); |
655 | kfree(objp: bus); |
656 | |
657 | return 0; |
658 | } |
659 | |
660 | static int ibm_configure_device(struct pci_func *func) |
661 | { |
662 | struct pci_bus *child; |
663 | int num; |
664 | int flag = 0; /* this is to make sure we don't double scan the bus, |
665 | for bridged devices primarily */ |
666 | |
667 | pci_lock_rescan_remove(); |
668 | |
669 | if (!(bus_structure_fixup(busno: func->busno))) |
670 | flag = 1; |
671 | if (func->dev == NULL) |
672 | func->dev = pci_get_domain_bus_and_slot(domain: 0, bus: func->busno, |
673 | PCI_DEVFN(func->device, func->function)); |
674 | |
675 | if (func->dev == NULL) { |
676 | struct pci_bus *bus = pci_find_bus(domain: 0, busnr: func->busno); |
677 | if (!bus) |
678 | goto out; |
679 | |
680 | num = pci_scan_slot(bus, |
681 | PCI_DEVFN(func->device, func->function)); |
682 | if (num) |
683 | pci_bus_add_devices(bus); |
684 | |
685 | func->dev = pci_get_domain_bus_and_slot(domain: 0, bus: func->busno, |
686 | PCI_DEVFN(func->device, func->function)); |
687 | if (func->dev == NULL) { |
688 | err("ERROR... : pci_dev still NULL\n" ); |
689 | goto out; |
690 | } |
691 | } |
692 | if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { |
693 | pci_hp_add_bridge(dev: func->dev); |
694 | child = func->dev->subordinate; |
695 | if (child) |
696 | pci_bus_add_devices(bus: child); |
697 | } |
698 | |
699 | out: |
700 | pci_unlock_rescan_remove(); |
701 | return 0; |
702 | } |
703 | |
704 | /******************************************************* |
705 | * Returns whether the bus is empty or not |
706 | *******************************************************/ |
707 | static int is_bus_empty(struct slot *slot_cur) |
708 | { |
709 | int rc; |
710 | struct slot *tmp_slot; |
711 | u8 i = slot_cur->bus_on->slot_min; |
712 | |
713 | while (i <= slot_cur->bus_on->slot_max) { |
714 | if (i == slot_cur->number) { |
715 | i++; |
716 | continue; |
717 | } |
718 | tmp_slot = ibmphp_get_slot_from_physical_num(i); |
719 | if (!tmp_slot) |
720 | return 0; |
721 | rc = slot_update(sl: &tmp_slot); |
722 | if (rc) |
723 | return 0; |
724 | if (SLOT_PRESENT(tmp_slot->status) && |
725 | SLOT_PWRGD(tmp_slot->status)) |
726 | return 0; |
727 | i++; |
728 | } |
729 | return 1; |
730 | } |
731 | |
732 | /*********************************************************** |
733 | * If the HPC permits and the bus currently empty, tries to set the |
734 | * bus speed and mode at the maximum card and bus capability |
735 | * Parameters: slot |
736 | * Returns: bus is set (0) or error code |
737 | ***********************************************************/ |
738 | static int set_bus(struct slot *slot_cur) |
739 | { |
740 | int rc; |
741 | u8 speed; |
742 | u8 cmd = 0x0; |
743 | int retval; |
744 | static const struct pci_device_id ciobx[] = { |
745 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 0x0101) }, |
746 | { }, |
747 | }; |
748 | |
749 | debug("%s - entry slot # %d\n" , __func__, slot_cur->number); |
750 | if (SET_BUS_STATUS(slot_cur->ctrl) && is_bus_empty(slot_cur)) { |
751 | rc = slot_update(sl: &slot_cur); |
752 | if (rc) |
753 | return rc; |
754 | speed = SLOT_SPEED(slot_cur->ext_status); |
755 | debug("ext_status = %x, speed = %x\n" , slot_cur->ext_status, speed); |
756 | switch (speed) { |
757 | case HPC_SLOT_SPEED_33: |
758 | cmd = HPC_BUS_33CONVMODE; |
759 | break; |
760 | case HPC_SLOT_SPEED_66: |
761 | if (SLOT_PCIX(slot_cur->ext_status)) { |
762 | if ((slot_cur->supported_speed >= BUS_SPEED_66) && |
763 | (slot_cur->supported_bus_mode == BUS_MODE_PCIX)) |
764 | cmd = HPC_BUS_66PCIXMODE; |
765 | else if (!SLOT_BUS_MODE(slot_cur->ext_status)) |
766 | /* if max slot/bus capability is 66 pci |
767 | and there's no bus mode mismatch, then |
768 | the adapter supports 66 pci */ |
769 | cmd = HPC_BUS_66CONVMODE; |
770 | else |
771 | cmd = HPC_BUS_33CONVMODE; |
772 | } else { |
773 | if (slot_cur->supported_speed >= BUS_SPEED_66) |
774 | cmd = HPC_BUS_66CONVMODE; |
775 | else |
776 | cmd = HPC_BUS_33CONVMODE; |
777 | } |
778 | break; |
779 | case HPC_SLOT_SPEED_133: |
780 | switch (slot_cur->supported_speed) { |
781 | case BUS_SPEED_33: |
782 | cmd = HPC_BUS_33CONVMODE; |
783 | break; |
784 | case BUS_SPEED_66: |
785 | if (slot_cur->supported_bus_mode == BUS_MODE_PCIX) |
786 | cmd = HPC_BUS_66PCIXMODE; |
787 | else |
788 | cmd = HPC_BUS_66CONVMODE; |
789 | break; |
790 | case BUS_SPEED_100: |
791 | cmd = HPC_BUS_100PCIXMODE; |
792 | break; |
793 | case BUS_SPEED_133: |
794 | /* This is to take care of the bug in CIOBX chip */ |
795 | if (pci_dev_present(ids: ciobx)) |
796 | ibmphp_hpc_writeslot(slot_cur, |
797 | HPC_BUS_100PCIXMODE); |
798 | cmd = HPC_BUS_133PCIXMODE; |
799 | break; |
800 | default: |
801 | err("Wrong bus speed\n" ); |
802 | return -ENODEV; |
803 | } |
804 | break; |
805 | default: |
806 | err("wrong slot speed\n" ); |
807 | return -ENODEV; |
808 | } |
809 | debug("setting bus speed for slot %d, cmd %x\n" , |
810 | slot_cur->number, cmd); |
811 | retval = ibmphp_hpc_writeslot(slot_cur, cmd); |
812 | if (retval) { |
813 | err("setting bus speed failed\n" ); |
814 | return retval; |
815 | } |
816 | if (CTLR_RESULT(slot_cur->ctrl->status)) { |
817 | err("command not completed successfully in set_bus\n" ); |
818 | return -EIO; |
819 | } |
820 | } |
821 | /* This is for x440, once Brandon fixes the firmware, |
822 | will not need this delay */ |
823 | msleep(msecs: 1000); |
824 | debug("%s -Exit\n" , __func__); |
825 | return 0; |
826 | } |
827 | |
828 | /* This routine checks the bus limitations that the slot is on from the BIOS. |
829 | * This is used in deciding whether or not to power up the slot. |
830 | * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on |
831 | * same bus) |
832 | * Parameters: slot |
833 | * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus |
834 | */ |
835 | static int check_limitations(struct slot *slot_cur) |
836 | { |
837 | u8 i; |
838 | struct slot *tmp_slot; |
839 | u8 count = 0; |
840 | u8 limitation = 0; |
841 | |
842 | for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) { |
843 | tmp_slot = ibmphp_get_slot_from_physical_num(i); |
844 | if (!tmp_slot) |
845 | return -ENODEV; |
846 | if ((SLOT_PWRGD(tmp_slot->status)) && |
847 | !(SLOT_CONNECT(tmp_slot->status))) |
848 | count++; |
849 | } |
850 | get_cur_bus_info(sl: &slot_cur); |
851 | switch (slot_cur->bus_on->current_speed) { |
852 | case BUS_SPEED_33: |
853 | limitation = slot_cur->bus_on->slots_at_33_conv; |
854 | break; |
855 | case BUS_SPEED_66: |
856 | if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX) |
857 | limitation = slot_cur->bus_on->slots_at_66_pcix; |
858 | else |
859 | limitation = slot_cur->bus_on->slots_at_66_conv; |
860 | break; |
861 | case BUS_SPEED_100: |
862 | limitation = slot_cur->bus_on->slots_at_100_pcix; |
863 | break; |
864 | case BUS_SPEED_133: |
865 | limitation = slot_cur->bus_on->slots_at_133_pcix; |
866 | break; |
867 | } |
868 | |
869 | if ((count + 1) > limitation) |
870 | return -EINVAL; |
871 | return 0; |
872 | } |
873 | |
874 | static inline void print_card_capability(struct slot *slot_cur) |
875 | { |
876 | info("capability of the card is " ); |
877 | if ((slot_cur->ext_status & CARD_INFO) == PCIX133) |
878 | info(" 133 MHz PCI-X\n" ); |
879 | else if ((slot_cur->ext_status & CARD_INFO) == PCIX66) |
880 | info(" 66 MHz PCI-X\n" ); |
881 | else if ((slot_cur->ext_status & CARD_INFO) == PCI66) |
882 | info(" 66 MHz PCI\n" ); |
883 | else |
884 | info(" 33 MHz PCI\n" ); |
885 | |
886 | } |
887 | |
888 | /* This routine will power on the slot, configure the device(s) and find the |
889 | * drivers for them. |
890 | * Parameters: hotplug_slot |
891 | * Returns: 0 or failure codes |
892 | */ |
893 | static int enable_slot(struct hotplug_slot *hs) |
894 | { |
895 | int rc, i, rcpr; |
896 | struct slot *slot_cur; |
897 | u8 function; |
898 | struct pci_func *tmp_func; |
899 | |
900 | ibmphp_lock_operations(); |
901 | |
902 | debug("ENABLING SLOT........\n" ); |
903 | slot_cur = to_slot(hotplug_slot: hs); |
904 | |
905 | rc = validate(slot_cur, ENABLE); |
906 | if (rc) { |
907 | err("validate function failed\n" ); |
908 | goto error_nopower; |
909 | } |
910 | |
911 | attn_LED_blink(slot_cur); |
912 | |
913 | rc = set_bus(slot_cur); |
914 | if (rc) { |
915 | err("was not able to set the bus\n" ); |
916 | goto error_nopower; |
917 | } |
918 | |
919 | /*-----------------debugging------------------------------*/ |
920 | get_cur_bus_info(sl: &slot_cur); |
921 | debug("the current bus speed right after set_bus = %x\n" , |
922 | slot_cur->bus_on->current_speed); |
923 | /*----------------------------------------------------------*/ |
924 | |
925 | rc = check_limitations(slot_cur); |
926 | if (rc) { |
927 | err("Adding this card exceeds the limitations of this bus.\n" ); |
928 | err("(i.e., >1 133MHz cards running on same bus, or >2 66 PCI cards running on same bus.\n" ); |
929 | err("Try hot-adding into another bus\n" ); |
930 | rc = -EINVAL; |
931 | goto error_nopower; |
932 | } |
933 | |
934 | rc = power_on(slot_cur); |
935 | |
936 | if (rc) { |
937 | err("something wrong when powering up... please see below for details\n" ); |
938 | /* need to turn off before on, otherwise, blinking overwrites */ |
939 | attn_off(slot_cur); |
940 | attn_on(slot_cur); |
941 | if (slot_update(sl: &slot_cur)) { |
942 | attn_off(slot_cur); |
943 | attn_on(slot_cur); |
944 | rc = -ENODEV; |
945 | goto exit; |
946 | } |
947 | /* Check to see the error of why it failed */ |
948 | if ((SLOT_POWER(slot_cur->status)) && |
949 | !(SLOT_PWRGD(slot_cur->status))) |
950 | err("power fault occurred trying to power up\n" ); |
951 | else if (SLOT_BUS_SPEED(slot_cur->status)) { |
952 | err("bus speed mismatch occurred. please check current bus speed and card capability\n" ); |
953 | print_card_capability(slot_cur); |
954 | } else if (SLOT_BUS_MODE(slot_cur->ext_status)) { |
955 | err("bus mode mismatch occurred. please check current bus mode and card capability\n" ); |
956 | print_card_capability(slot_cur); |
957 | } |
958 | ibmphp_update_slot_info(slot_cur); |
959 | goto exit; |
960 | } |
961 | debug("after power_on\n" ); |
962 | /*-----------------------debugging---------------------------*/ |
963 | get_cur_bus_info(sl: &slot_cur); |
964 | debug("the current bus speed right after power_on = %x\n" , |
965 | slot_cur->bus_on->current_speed); |
966 | /*----------------------------------------------------------*/ |
967 | |
968 | rc = slot_update(sl: &slot_cur); |
969 | if (rc) |
970 | goto error_power; |
971 | |
972 | rc = -EINVAL; |
973 | if (SLOT_POWER(slot_cur->status) && !(SLOT_PWRGD(slot_cur->status))) { |
974 | err("power fault occurred trying to power up...\n" ); |
975 | goto error_power; |
976 | } |
977 | if (SLOT_POWER(slot_cur->status) && (SLOT_BUS_SPEED(slot_cur->status))) { |
978 | err("bus speed mismatch occurred. please check current bus speed and card capability\n" ); |
979 | print_card_capability(slot_cur); |
980 | goto error_power; |
981 | } |
982 | /* Don't think this case will happen after above checks... |
983 | * but just in case, for paranoia sake */ |
984 | if (!(SLOT_POWER(slot_cur->status))) { |
985 | err("power on failed...\n" ); |
986 | goto error_power; |
987 | } |
988 | |
989 | slot_cur->func = kzalloc(size: sizeof(struct pci_func), GFP_KERNEL); |
990 | if (!slot_cur->func) { |
991 | /* do update_slot_info here? */ |
992 | rc = -ENOMEM; |
993 | goto error_power; |
994 | } |
995 | slot_cur->func->busno = slot_cur->bus; |
996 | slot_cur->func->device = slot_cur->device; |
997 | for (i = 0; i < 4; i++) |
998 | slot_cur->func->irq[i] = slot_cur->irq[i]; |
999 | |
1000 | debug("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n" , |
1001 | slot_cur->bus, slot_cur->device); |
1002 | |
1003 | if (ibmphp_configure_card(slot_cur->func, slot_cur->number)) { |
1004 | err("configure_card was unsuccessful...\n" ); |
1005 | /* true because don't need to actually deallocate resources, |
1006 | * just remove references */ |
1007 | ibmphp_unconfigure_card(&slot_cur, 1); |
1008 | debug("after unconfigure_card\n" ); |
1009 | slot_cur->func = NULL; |
1010 | rc = -ENOMEM; |
1011 | goto error_power; |
1012 | } |
1013 | |
1014 | function = 0x00; |
1015 | do { |
1016 | tmp_func = ibm_slot_find(busno: slot_cur->bus, device: slot_cur->func->device, |
1017 | function: function++); |
1018 | if (tmp_func && !(tmp_func->dev)) |
1019 | ibm_configure_device(func: tmp_func); |
1020 | } while (tmp_func); |
1021 | |
1022 | attn_off(slot_cur); |
1023 | if (slot_update(sl: &slot_cur)) { |
1024 | rc = -EFAULT; |
1025 | goto exit; |
1026 | } |
1027 | ibmphp_print_test(); |
1028 | rc = ibmphp_update_slot_info(slot_cur); |
1029 | exit: |
1030 | ibmphp_unlock_operations(); |
1031 | return rc; |
1032 | |
1033 | error_nopower: |
1034 | attn_off(slot_cur); /* need to turn off if was blinking b4 */ |
1035 | attn_on(slot_cur); |
1036 | error_cont: |
1037 | rcpr = slot_update(sl: &slot_cur); |
1038 | if (rcpr) { |
1039 | rc = rcpr; |
1040 | goto exit; |
1041 | } |
1042 | ibmphp_update_slot_info(slot_cur); |
1043 | goto exit; |
1044 | |
1045 | error_power: |
1046 | attn_off(slot_cur); /* need to turn off if was blinking b4 */ |
1047 | attn_on(slot_cur); |
1048 | rcpr = power_off(slot_cur); |
1049 | if (rcpr) { |
1050 | rc = rcpr; |
1051 | goto exit; |
1052 | } |
1053 | goto error_cont; |
1054 | } |
1055 | |
1056 | /************************************************************** |
1057 | * HOT REMOVING ADAPTER CARD * |
1058 | * INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE * |
1059 | * OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE * |
1060 | * DISABLE POWER , * |
1061 | **************************************************************/ |
1062 | static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot) |
1063 | { |
1064 | struct slot *slot = to_slot(hotplug_slot); |
1065 | int rc; |
1066 | |
1067 | ibmphp_lock_operations(); |
1068 | rc = ibmphp_do_disable_slot(slot_cur: slot); |
1069 | ibmphp_unlock_operations(); |
1070 | return rc; |
1071 | } |
1072 | |
1073 | int ibmphp_do_disable_slot(struct slot *slot_cur) |
1074 | { |
1075 | int rc; |
1076 | u8 flag; |
1077 | |
1078 | debug("DISABLING SLOT...\n" ); |
1079 | |
1080 | if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) |
1081 | return -ENODEV; |
1082 | |
1083 | flag = slot_cur->flag; |
1084 | slot_cur->flag = 1; |
1085 | |
1086 | if (flag == 1) { |
1087 | rc = validate(slot_cur, DISABLE); |
1088 | /* checking if powered off already & valid slot # */ |
1089 | if (rc) |
1090 | goto error; |
1091 | } |
1092 | attn_LED_blink(slot_cur); |
1093 | |
1094 | if (slot_cur->func == NULL) { |
1095 | /* We need this for functions that were there on bootup */ |
1096 | slot_cur->func = kzalloc(size: sizeof(struct pci_func), GFP_KERNEL); |
1097 | if (!slot_cur->func) { |
1098 | rc = -ENOMEM; |
1099 | goto error; |
1100 | } |
1101 | slot_cur->func->busno = slot_cur->bus; |
1102 | slot_cur->func->device = slot_cur->device; |
1103 | } |
1104 | |
1105 | ibm_unconfigure_device(func: slot_cur->func); |
1106 | |
1107 | /* |
1108 | * If we got here from latch suddenly opening on operating card or |
1109 | * a power fault, there's no power to the card, so cannot |
1110 | * read from it to determine what resources it occupied. This operation |
1111 | * is forbidden anyhow. The best we can do is remove it from kernel |
1112 | * lists at least */ |
1113 | |
1114 | if (!flag) { |
1115 | attn_off(slot_cur); |
1116 | return 0; |
1117 | } |
1118 | |
1119 | rc = ibmphp_unconfigure_card(&slot_cur, 0); |
1120 | slot_cur->func = NULL; |
1121 | debug("in disable_slot. after unconfigure_card\n" ); |
1122 | if (rc) { |
1123 | err("could not unconfigure card.\n" ); |
1124 | goto error; |
1125 | } |
1126 | |
1127 | rc = ibmphp_hpc_writeslot(slot_cur, HPC_SLOT_OFF); |
1128 | if (rc) |
1129 | goto error; |
1130 | |
1131 | attn_off(slot_cur); |
1132 | rc = slot_update(sl: &slot_cur); |
1133 | if (rc) |
1134 | goto exit; |
1135 | |
1136 | rc = ibmphp_update_slot_info(slot_cur); |
1137 | ibmphp_print_test(); |
1138 | exit: |
1139 | return rc; |
1140 | |
1141 | error: |
1142 | /* Need to turn off if was blinking b4 */ |
1143 | attn_off(slot_cur); |
1144 | attn_on(slot_cur); |
1145 | if (slot_update(sl: &slot_cur)) { |
1146 | rc = -EFAULT; |
1147 | goto exit; |
1148 | } |
1149 | if (flag) |
1150 | ibmphp_update_slot_info(slot_cur); |
1151 | goto exit; |
1152 | } |
1153 | |
1154 | const struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { |
1155 | .set_attention_status = set_attention_status, |
1156 | .enable_slot = enable_slot, |
1157 | .disable_slot = ibmphp_disable_slot, |
1158 | .hardware_test = NULL, |
1159 | .get_power_status = get_power_status, |
1160 | .get_attention_status = get_attention_status, |
1161 | .get_latch_status = get_latch_status, |
1162 | .get_adapter_status = get_adapter_present, |
1163 | }; |
1164 | |
1165 | static void ibmphp_unload(void) |
1166 | { |
1167 | free_slots(); |
1168 | debug("after slots\n" ); |
1169 | ibmphp_free_resources(); |
1170 | debug("after resources\n" ); |
1171 | ibmphp_free_bus_info_queue(); |
1172 | debug("after bus info\n" ); |
1173 | ibmphp_free_ebda_hpc_queue(); |
1174 | debug("after ebda hpc\n" ); |
1175 | ibmphp_free_ebda_pci_rsrc_queue(); |
1176 | debug("after ebda pci rsrc\n" ); |
1177 | kfree(objp: ibmphp_pci_bus); |
1178 | } |
1179 | |
1180 | static int __init ibmphp_init(void) |
1181 | { |
1182 | struct pci_bus *bus; |
1183 | int i = 0; |
1184 | int rc = 0; |
1185 | |
1186 | init_flag = 1; |
1187 | |
1188 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n" ); |
1189 | |
1190 | ibmphp_pci_bus = kmalloc(size: sizeof(*ibmphp_pci_bus), GFP_KERNEL); |
1191 | if (!ibmphp_pci_bus) { |
1192 | rc = -ENOMEM; |
1193 | goto exit; |
1194 | } |
1195 | |
1196 | bus = pci_find_bus(domain: 0, busnr: 0); |
1197 | if (!bus) { |
1198 | err("Can't find the root pci bus, can not continue\n" ); |
1199 | rc = -ENODEV; |
1200 | goto error; |
1201 | } |
1202 | memcpy(ibmphp_pci_bus, bus, sizeof(*ibmphp_pci_bus)); |
1203 | |
1204 | ibmphp_debug = debug; |
1205 | |
1206 | for (i = 0; i < 16; i++) |
1207 | irqs[i] = 0; |
1208 | |
1209 | rc = ibmphp_access_ebda(); |
1210 | if (rc) |
1211 | goto error; |
1212 | debug("after ibmphp_access_ebda()\n" ); |
1213 | |
1214 | rc = ibmphp_rsrc_init(); |
1215 | if (rc) |
1216 | goto error; |
1217 | debug("AFTER Resource & EBDA INITIALIZATIONS\n" ); |
1218 | |
1219 | max_slots = get_max_slots(); |
1220 | |
1221 | rc = ibmphp_register_pci(); |
1222 | if (rc) |
1223 | goto error; |
1224 | |
1225 | if (init_ops()) { |
1226 | rc = -ENODEV; |
1227 | goto error; |
1228 | } |
1229 | |
1230 | ibmphp_print_test(); |
1231 | rc = ibmphp_hpc_start_poll_thread(); |
1232 | if (rc) |
1233 | goto error; |
1234 | |
1235 | exit: |
1236 | return rc; |
1237 | |
1238 | error: |
1239 | ibmphp_unload(); |
1240 | goto exit; |
1241 | } |
1242 | |
1243 | static void __exit ibmphp_exit(void) |
1244 | { |
1245 | ibmphp_hpc_stop_poll_thread(); |
1246 | debug("after polling\n" ); |
1247 | ibmphp_unload(); |
1248 | debug("done\n" ); |
1249 | } |
1250 | |
1251 | module_init(ibmphp_init); |
1252 | module_exit(ibmphp_exit); |
1253 | |