Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.git] / drivers / pci / hotplug / cpci_hotplug_core.c
1 /*
2  * CompactPCI Hot Plug Driver
3  *
4  * Copyright (C) 2002 SOMA Networks, Inc.
5  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
6  * Copyright (C) 2001 IBM Corp.
7  *
8  * All rights reserved.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or (at
13  * your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
18  * NON INFRINGEMENT.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  * Send feedback to <scottm@somanetworks.com>
26  */
27
28 #include <linux/config.h>
29 #include <linux/module.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/pci.h>
33 #include <linux/init.h>
34 #include <linux/interrupt.h>
35 #include <linux/smp_lock.h>
36 #include <linux/delay.h>
37 #include "pci_hotplug.h"
38 #include "cpci_hotplug.h"
39
40 #define DRIVER_VERSION  "0.2"
41 #define DRIVER_AUTHOR   "Scott Murray <scottm@somanetworks.com>"
42 #define DRIVER_DESC     "CompactPCI Hot Plug Core"
43
44 #define MY_NAME "cpci_hotplug"
45
46 #define dbg(format, arg...)                                     \
47         do {                                                    \
48                 if(cpci_debug)                                  \
49                         printk (KERN_DEBUG "%s: " format "\n",  \
50                                 MY_NAME , ## arg);              \
51         } while(0)
52 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
53 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
54 #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
55
56 /* local variables */
57 static spinlock_t list_lock;
58 static LIST_HEAD(slot_list);
59 static int slots;
60 int cpci_debug;
61 static struct cpci_hp_controller *controller;
62 static struct semaphore event_semaphore;        /* mutex for process loop (up if something to process) */
63 static struct semaphore thread_exit;            /* guard ensure thread has exited before calling it quits */
64 static int thread_finished = 1;
65
66 static int enable_slot(struct hotplug_slot *slot);
67 static int disable_slot(struct hotplug_slot *slot);
68 static int set_attention_status(struct hotplug_slot *slot, u8 value);
69 static int get_power_status(struct hotplug_slot *slot, u8 * value);
70 static int get_attention_status(struct hotplug_slot *slot, u8 * value);
71
72 static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
73         .owner = THIS_MODULE,
74         .enable_slot = enable_slot,
75         .disable_slot = disable_slot,
76         .set_attention_status = set_attention_status,
77         .get_power_status = get_power_status,
78         .get_attention_status = get_attention_status,
79 };
80
81 static int
82 update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
83 {
84         struct hotplug_slot_info info;
85
86         memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
87         info.latch_status = value;
88         return pci_hp_change_slot_info(hotplug_slot, &info);
89 }
90
91 static int
92 update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
93 {
94         struct hotplug_slot_info info;
95
96         memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
97         info.adapter_status = value;
98         return pci_hp_change_slot_info(hotplug_slot, &info);
99 }
100
101 static int
102 enable_slot(struct hotplug_slot *hotplug_slot)
103 {
104         struct slot *slot = hotplug_slot->private;
105         int retval = 0;
106
107         dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
108
109         if(controller->ops->set_power) {
110                 retval = controller->ops->set_power(slot, 1);
111         }
112
113         return retval;
114 }
115
116 static int
117 disable_slot(struct hotplug_slot *hotplug_slot)
118 {
119         struct slot *slot = hotplug_slot->private;
120         int retval = 0;
121
122         dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
123
124         /* Unconfigure device */
125         dbg("%s - unconfiguring slot %s",
126             __FUNCTION__, slot->hotplug_slot->name);
127         if((retval = cpci_unconfigure_slot(slot))) {
128                 err("%s - could not unconfigure slot %s",
129                     __FUNCTION__, slot->hotplug_slot->name);
130                 return retval;
131         }
132         dbg("%s - finished unconfiguring slot %s",
133             __FUNCTION__, slot->hotplug_slot->name);
134
135         /* Clear EXT (by setting it) */
136         if(cpci_clear_ext(slot)) {
137                 err("%s - could not clear EXT for slot %s",
138                     __FUNCTION__, slot->hotplug_slot->name);
139                 retval = -ENODEV;
140         }
141         cpci_led_on(slot);
142
143         if(controller->ops->set_power) {
144                 retval = controller->ops->set_power(slot, 0);
145         }
146
147         if(update_adapter_status(slot->hotplug_slot, 0)) {
148                 warn("failure to update adapter file");
149         }
150
151         slot->extracting = 0;
152
153         return retval;
154 }
155
156 static u8
157 cpci_get_power_status(struct slot *slot)
158 {
159         u8 power = 1;
160
161         if(controller->ops->get_power) {
162                 power = controller->ops->get_power(slot);
163         }
164         return power;
165 }
166
167 static int
168 get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
169 {
170         struct slot *slot = hotplug_slot->private;
171
172         *value = cpci_get_power_status(slot);
173         return 0;
174 }
175
176 static int
177 get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
178 {
179         struct slot *slot = hotplug_slot->private;
180
181         *value = cpci_get_attention_status(slot);
182         return 0;
183 }
184
185 static int
186 set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
187 {
188         return cpci_set_attention_status(hotplug_slot->private, status);
189 }
190
191 static void release_slot(struct hotplug_slot *hotplug_slot)
192 {
193         struct slot *slot = hotplug_slot->private;
194
195         kfree(slot->hotplug_slot->info);
196         kfree(slot->hotplug_slot->name);
197         kfree(slot->hotplug_slot);
198         kfree(slot);
199 }
200
201 #define SLOT_NAME_SIZE  6
202 static void
203 make_slot_name(struct slot *slot)
204 {
205         snprintf(slot->hotplug_slot->name,
206                  SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number);
207 }
208
209 int
210 cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
211 {
212         struct slot *slot;
213         struct hotplug_slot *hotplug_slot;
214         struct hotplug_slot_info *info;
215         char *name;
216         int status = -ENOMEM;
217         int i;
218
219         if(!(controller && bus)) {
220                 return -ENODEV;
221         }
222
223         /*
224          * Create a structure for each slot, and register that slot
225          * with the pci_hotplug subsystem.
226          */
227         for (i = first; i <= last; ++i) {
228                 slot = kmalloc(sizeof (struct slot), GFP_KERNEL);
229                 if (!slot)
230                         goto error;
231                 memset(slot, 0, sizeof (struct slot));
232
233                 hotplug_slot =
234                     kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
235                 if (!hotplug_slot)
236                         goto error_slot;
237                 memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
238                 slot->hotplug_slot = hotplug_slot;
239
240                 info = kmalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
241                 if (!info)
242                         goto error_hpslot;
243                 memset(info, 0, sizeof (struct hotplug_slot_info));
244                 hotplug_slot->info = info;
245
246                 name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
247                 if (!name)
248                         goto error_info;
249                 hotplug_slot->name = name;
250
251                 slot->bus = bus;
252                 slot->number = i;
253                 slot->devfn = PCI_DEVFN(i, 0);
254
255                 hotplug_slot->private = slot;
256                 hotplug_slot->release = &release_slot;
257                 make_slot_name(slot);
258                 hotplug_slot->ops = &cpci_hotplug_slot_ops;
259
260                 /*
261                  * Initialize the slot info structure with some known
262                  * good values.
263                  */
264                 dbg("initializing slot %s", slot->hotplug_slot->name);
265                 info->power_status = cpci_get_power_status(slot);
266                 info->attention_status = cpci_get_attention_status(slot);
267
268                 dbg("registering slot %s", slot->hotplug_slot->name);
269                 status = pci_hp_register(slot->hotplug_slot);
270                 if (status) {
271                         err("pci_hp_register failed with error %d", status);
272                         goto error_name;
273                 }
274
275                 /* Add slot to our internal list */
276                 spin_lock(&list_lock);
277                 list_add(&slot->slot_list, &slot_list);
278                 slots++;
279                 spin_unlock(&list_lock);
280         }
281         return 0;
282 error_name:
283         kfree(name);
284 error_info:
285         kfree(info);
286 error_hpslot:
287         kfree(hotplug_slot);
288 error_slot:
289         kfree(slot);
290 error:
291         return status;
292 }
293
294 int
295 cpci_hp_unregister_bus(struct pci_bus *bus)
296 {
297         struct slot *slot;
298         struct list_head *tmp;
299         struct list_head *next;
300         int status;
301
302         spin_lock(&list_lock);
303         if(!slots) {
304                 spin_unlock(&list_lock);
305                 return -1;
306         }
307         list_for_each_safe(tmp, next, &slot_list) {
308                 slot = list_entry(tmp, struct slot, slot_list);
309                 if(slot->bus == bus) {
310                         dbg("deregistering slot %s", slot->hotplug_slot->name);
311                         status = pci_hp_deregister(slot->hotplug_slot);
312                         if(status) {
313                                 err("pci_hp_deregister failed with error %d",
314                                     status);
315                                 return status;
316                         }
317
318                         list_del(&slot->slot_list);
319                         slots--;
320                 }
321         }
322         spin_unlock(&list_lock);
323         return 0;
324 }
325
326 /* This is the interrupt mode interrupt handler */
327 static irqreturn_t
328 cpci_hp_intr(int irq, void *data, struct pt_regs *regs)
329 {
330         dbg("entered cpci_hp_intr");
331
332         /* Check to see if it was our interrupt */
333         if((controller->irq_flags & SA_SHIRQ) &&
334             !controller->ops->check_irq(controller->dev_id)) {
335                 dbg("exited cpci_hp_intr, not our interrupt");
336                 return IRQ_NONE;
337         }
338
339         /* Disable ENUM interrupt */
340         controller->ops->disable_irq();
341
342         /* Trigger processing by the event thread */
343         dbg("Signal event_semaphore");
344         up(&event_semaphore);
345         dbg("exited cpci_hp_intr");
346         return IRQ_HANDLED;
347 }
348
349 /*
350  * According to PICMG 2.12 R2.0, section 6.3.2, upon
351  * initialization, the system driver shall clear the
352  * INS bits of the cold-inserted devices.
353  */
354 static int
355 init_slots(void)
356 {
357         struct slot *slot;
358         struct list_head *tmp;
359         struct pci_dev* dev;
360
361         dbg("%s - enter", __FUNCTION__);
362         spin_lock(&list_lock);
363         if(!slots) {
364                 spin_unlock(&list_lock);
365                 return -1;
366         }
367         list_for_each(tmp, &slot_list) {
368                 slot = list_entry(tmp, struct slot, slot_list);
369                 dbg("%s - looking at slot %s",
370                     __FUNCTION__, slot->hotplug_slot->name);
371                 if(cpci_check_and_clear_ins(slot)) {
372                         dbg("%s - cleared INS for slot %s",
373                             __FUNCTION__, slot->hotplug_slot->name);
374                         dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0));
375                         if(dev) {
376                                 if(update_adapter_status(slot->hotplug_slot, 1)) {
377                                         warn("failure to update adapter file");
378                                 }
379                                 if(update_latch_status(slot->hotplug_slot, 1)) {
380                                         warn("failure to update latch file");
381                                 }
382                                 slot->dev = dev;
383                         } else {
384                                 err("%s - no driver attached to device in slot %s",
385                                     __FUNCTION__, slot->hotplug_slot->name);
386                         }
387                 }
388         }
389         spin_unlock(&list_lock);
390         dbg("%s - exit", __FUNCTION__);
391         return 0;
392 }
393
394 static int
395 check_slots(void)
396 {
397         struct slot *slot;
398         struct list_head *tmp;
399         int extracted;
400         int inserted;
401
402         spin_lock(&list_lock);
403         if(!slots) {
404                 spin_unlock(&list_lock);
405                 err("no slots registered, shutting down");
406                 return -1;
407         }
408         extracted = inserted = 0;
409         list_for_each(tmp, &slot_list) {
410                 slot = list_entry(tmp, struct slot, slot_list);
411                 dbg("%s - looking at slot %s",
412                     __FUNCTION__, slot->hotplug_slot->name);
413                 if(cpci_check_and_clear_ins(slot)) {
414                         u16 hs_csr;
415
416                         /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */
417                         if(slot->dev) {
418                                 warn("slot %s already inserted", slot->hotplug_slot->name);
419                                 inserted++;
420                                 continue;
421                         }
422
423                         /* Process insertion */
424                         dbg("%s - slot %s inserted",
425                             __FUNCTION__, slot->hotplug_slot->name);
426
427                         /* GSM, debug */
428                         hs_csr = cpci_get_hs_csr(slot);
429                         dbg("%s - slot %s HS_CSR (1) = %04x",
430                             __FUNCTION__, slot->hotplug_slot->name, hs_csr);
431
432                         /* Configure device */
433                         dbg("%s - configuring slot %s",
434                             __FUNCTION__, slot->hotplug_slot->name);
435                         if(cpci_configure_slot(slot)) {
436                                 err("%s - could not configure slot %s",
437                                     __FUNCTION__, slot->hotplug_slot->name);
438                                 continue;
439                         }
440                         dbg("%s - finished configuring slot %s",
441                             __FUNCTION__, slot->hotplug_slot->name);
442
443                         /* GSM, debug */
444                         hs_csr = cpci_get_hs_csr(slot);
445                         dbg("%s - slot %s HS_CSR (2) = %04x",
446                             __FUNCTION__, slot->hotplug_slot->name, hs_csr);
447
448                         if(update_latch_status(slot->hotplug_slot, 1)) {
449                                 warn("failure to update latch file");
450                         }
451
452                         if(update_adapter_status(slot->hotplug_slot, 1)) {
453                                 warn("failure to update adapter file");
454                         }
455
456                         cpci_led_off(slot);
457
458                         /* GSM, debug */
459                         hs_csr = cpci_get_hs_csr(slot);
460                         dbg("%s - slot %s HS_CSR (3) = %04x",
461                             __FUNCTION__, slot->hotplug_slot->name, hs_csr);
462
463                         inserted++;
464                 } else if(cpci_check_ext(slot)) {
465                         u16 hs_csr;
466
467                         /* Process extraction request */
468                         dbg("%s - slot %s extracted",
469                             __FUNCTION__, slot->hotplug_slot->name);
470
471                         /* GSM, debug */
472                         hs_csr = cpci_get_hs_csr(slot);
473                         dbg("%s - slot %s HS_CSR = %04x",
474                             __FUNCTION__, slot->hotplug_slot->name, hs_csr);
475
476                         if(!slot->extracting) {
477                                 if(update_latch_status(slot->hotplug_slot, 0)) {
478                                         warn("failure to update latch file");
479                                 }
480                                 slot->extracting = 1;
481                         }
482                         extracted++;
483                 }
484         }
485         spin_unlock(&list_lock);
486         if(inserted || extracted) {
487                 return extracted;
488         }
489         else {
490                 err("cannot find ENUM# source, shutting down");
491                 return -1;
492         }
493 }
494
495 /* This is the interrupt mode worker thread body */
496 static int
497 event_thread(void *data)
498 {
499         int rc;
500         struct slot *slot;
501         struct list_head *tmp;
502
503         lock_kernel();
504         daemonize("cpci_hp_eventd");
505         unlock_kernel();
506
507         dbg("%s - event thread started", __FUNCTION__);
508         while(1) {
509                 dbg("event thread sleeping");
510                 down_interruptible(&event_semaphore);
511                 dbg("event thread woken, thread_finished = %d",
512                     thread_finished);
513                 if(thread_finished || signal_pending(current))
514                         break;
515                 while(controller->ops->query_enum()) {
516                         rc = check_slots();
517                         if (rc > 0)
518                                 /* Give userspace a chance to handle extraction */
519                                 msleep(500);
520                         else if (rc < 0) {
521                                 dbg("%s - error checking slots", __FUNCTION__);
522                                 thread_finished = 1;
523                                 break;
524                         }
525                 }
526                 /* Check for someone yanking out a board */
527                 list_for_each(tmp, &slot_list) {
528                         slot = list_entry(tmp, struct slot, slot_list);
529                         if(slot->extracting) {
530                                 /*
531                                  * Hmmm, we're likely hosed at this point, should we
532                                  * bother trying to tell the driver or not?
533                                  */
534                                 err("card in slot %s was improperly removed",
535                                     slot->hotplug_slot->name);
536                                 if(update_adapter_status(slot->hotplug_slot, 0)) {
537                                         warn("failure to update adapter file");
538                                 }
539                                 slot->extracting = 0;
540                         }
541                 }
542
543                 /* Re-enable ENUM# interrupt */
544                 dbg("%s - re-enabling irq", __FUNCTION__);
545                 controller->ops->enable_irq();
546         }
547
548         dbg("%s - event thread signals exit", __FUNCTION__);
549         up(&thread_exit);
550         return 0;
551 }
552
553 /* This is the polling mode worker thread body */
554 static int
555 poll_thread(void *data)
556 {
557         int rc;
558         struct slot *slot;
559         struct list_head *tmp;
560
561         lock_kernel();
562         daemonize("cpci_hp_polld");
563         unlock_kernel();
564
565         while(1) {
566                 if(thread_finished || signal_pending(current))
567                         break;
568
569                 while(controller->ops->query_enum()) {
570                         rc = check_slots();
571                         if(rc > 0)
572                                 /* Give userspace a chance to handle extraction */
573                                 msleep(500);
574                         else if (rc < 0) {
575                                 dbg("%s - error checking slots", __FUNCTION__);
576                                 thread_finished = 1;
577                                 break;
578                         }
579                 }
580                 /* Check for someone yanking out a board */
581                 list_for_each(tmp, &slot_list) {
582                         slot = list_entry(tmp, struct slot, slot_list);
583                         if(slot->extracting) {
584                                 /*
585                                  * Hmmm, we're likely hosed at this point, should we
586                                  * bother trying to tell the driver or not?
587                                  */
588                                 err("card in slot %s was improperly removed",
589                                     slot->hotplug_slot->name);
590                                 if(update_adapter_status(slot->hotplug_slot, 0)) {
591                                         warn("failure to update adapter file");
592                                 }
593                                 slot->extracting = 0;
594                         }
595                 }
596
597                 msleep(100);
598         }
599         dbg("poll thread signals exit");
600         up(&thread_exit);
601         return 0;
602 }
603
604 static int
605 cpci_start_thread(void)
606 {
607         int pid;
608
609         /* initialize our semaphores */
610         init_MUTEX_LOCKED(&event_semaphore);
611         init_MUTEX_LOCKED(&thread_exit);
612         thread_finished = 0;
613
614         if(controller->irq) {
615                 pid = kernel_thread(event_thread, NULL, 0);
616         } else {
617                 pid = kernel_thread(poll_thread, NULL, 0);
618         }
619         if(pid < 0) {
620                 err("Can't start up our thread");
621                 return -1;
622         }
623         dbg("Our thread pid = %d", pid);
624         return 0;
625 }
626
627 static void
628 cpci_stop_thread(void)
629 {
630         thread_finished = 1;
631         dbg("thread finish command given");
632         if(controller->irq) {
633                 up(&event_semaphore);
634         }
635         dbg("wait for thread to exit");
636         down(&thread_exit);
637 }
638
639 int
640 cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
641 {
642         int status = 0;
643
644         if(!controller) {
645                 controller = new_controller;
646                 if(controller->irq) {
647                         if(request_irq(controller->irq,
648                                         cpci_hp_intr,
649                                         controller->irq_flags,
650                                         MY_NAME, controller->dev_id)) {
651                                 err("Can't get irq %d for the hotplug cPCI controller", controller->irq);
652                                 status = -ENODEV;
653                         }
654                         dbg("%s - acquired controller irq %d", __FUNCTION__,
655                             controller->irq);
656                 }
657         } else {
658                 err("cPCI hotplug controller already registered");
659                 status = -1;
660         }
661         return status;
662 }
663
664 int
665 cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
666 {
667         int status = 0;
668
669         if(controller) {
670                 if(!thread_finished) {
671                         cpci_stop_thread();
672                 }
673                 if(controller->irq) {
674                         free_irq(controller->irq, controller->dev_id);
675                 }
676                 controller = NULL;
677         } else {
678                 status = -ENODEV;
679         }
680         return status;
681 }
682
683 int
684 cpci_hp_start(void)
685 {
686         static int first = 1;
687         int status;
688
689         dbg("%s - enter", __FUNCTION__);
690         if(!controller) {
691                 return -ENODEV;
692         }
693
694         spin_lock(&list_lock);
695         if(!slots) {
696                 spin_unlock(&list_lock);
697                 return -ENODEV;
698         }
699         spin_unlock(&list_lock);
700
701         if(first) {
702                 status = init_slots();
703                 if(status) {
704                         return status;
705                 }
706                 first = 0;
707         }
708
709         status = cpci_start_thread();
710         if(status) {
711                 return status;
712         }
713         dbg("%s - thread started", __FUNCTION__);
714
715         if(controller->irq) {
716                 /* Start enum interrupt processing */
717                 dbg("%s - enabling irq", __FUNCTION__);
718                 controller->ops->enable_irq();
719         }
720         dbg("%s - exit", __FUNCTION__);
721         return 0;
722 }
723
724 int
725 cpci_hp_stop(void)
726 {
727         if(!controller) {
728                 return -ENODEV;
729         }
730
731         if(controller->irq) {
732                 /* Stop enum interrupt processing */
733                 dbg("%s - disabling irq", __FUNCTION__);
734                 controller->ops->disable_irq();
735         }
736         cpci_stop_thread();
737         return 0;
738 }
739
740 static void __exit
741 cleanup_slots(void)
742 {
743         struct list_head *tmp;
744         struct slot *slot;
745
746         /*
747          * Unregister all of our slots with the pci_hotplug subsystem,
748          * and free up all memory that we had allocated.
749          */
750         spin_lock(&list_lock);
751         if(!slots) {
752                 goto null_cleanup;
753         }
754         list_for_each(tmp, &slot_list) {
755                 slot = list_entry(tmp, struct slot, slot_list);
756                 list_del(&slot->slot_list);
757                 pci_hp_deregister(slot->hotplug_slot);
758                 kfree(slot->hotplug_slot->info);
759                 kfree(slot->hotplug_slot->name);
760                 kfree(slot->hotplug_slot);
761                 kfree(slot);
762         }
763       null_cleanup:
764         spin_unlock(&list_lock);
765         return;
766 }
767
768 int __init
769 cpci_hotplug_init(int debug)
770 {
771         spin_lock_init(&list_lock);
772         cpci_debug = debug;
773
774         info(DRIVER_DESC " version: " DRIVER_VERSION);
775         return 0;
776 }
777
778 void __exit
779 cpci_hotplug_exit(void)
780 {
781         /*
782          * Clean everything up.
783          */
784         cleanup_slots();
785 }
786
787 EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
788 EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
789 EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
790 EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
791 EXPORT_SYMBOL_GPL(cpci_hp_start);
792 EXPORT_SYMBOL_GPL(cpci_hp_stop);