• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * CompactPCI Hot Plug Driver
3  *
4  * Copyright (C) 2002,2005 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/module.h>
29 #include <linux/kernel.h>
30 #include <linux/slab.h>
31 #include <linux/pci.h>
32 #include <linux/pci_hotplug.h>
33 #include <linux/init.h>
34 #include <linux/interrupt.h>
35 #include <linux/smp_lock.h>
36 #include <asm/atomic.h>
37 #include <linux/delay.h>
38 #include <linux/kthread.h>
39 #include "cpci_hotplug.h"
40 
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 DECLARE_RWSEM(list_rwsem);
58 static LIST_HEAD(slot_list);
59 static int slots;
60 static atomic_t extracting;
61 int cpci_debug;
62 static struct cpci_hp_controller *controller;
63 static struct task_struct *cpci_thread;
64 static int thread_finished;
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 static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
72 static int get_latch_status(struct hotplug_slot *slot, u8 * value);
73 
74 static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
75 	.owner = THIS_MODULE,
76 	.enable_slot = enable_slot,
77 	.disable_slot = disable_slot,
78 	.set_attention_status = set_attention_status,
79 	.get_power_status = get_power_status,
80 	.get_attention_status = get_attention_status,
81 	.get_adapter_status = get_adapter_status,
82 	.get_latch_status = get_latch_status,
83 };
84 
85 static int
update_latch_status(struct hotplug_slot * hotplug_slot,u8 value)86 update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
87 {
88 	struct hotplug_slot_info info;
89 
90 	memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
91 	info.latch_status = value;
92 	return pci_hp_change_slot_info(hotplug_slot, &info);
93 }
94 
95 static int
update_adapter_status(struct hotplug_slot * hotplug_slot,u8 value)96 update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
97 {
98 	struct hotplug_slot_info info;
99 
100 	memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
101 	info.adapter_status = value;
102 	return pci_hp_change_slot_info(hotplug_slot, &info);
103 }
104 
105 static int
enable_slot(struct hotplug_slot * hotplug_slot)106 enable_slot(struct hotplug_slot *hotplug_slot)
107 {
108 	struct slot *slot = hotplug_slot->private;
109 	int retval = 0;
110 
111 	dbg("%s - physical_slot = %s", __func__, slot_name(slot));
112 
113 	if (controller->ops->set_power)
114 		retval = controller->ops->set_power(slot, 1);
115 	return retval;
116 }
117 
118 static int
disable_slot(struct hotplug_slot * hotplug_slot)119 disable_slot(struct hotplug_slot *hotplug_slot)
120 {
121 	struct slot *slot = hotplug_slot->private;
122 	int retval = 0;
123 
124 	dbg("%s - physical_slot = %s", __func__, slot_name(slot));
125 
126 	down_write(&list_rwsem);
127 
128 	/* Unconfigure device */
129 	dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
130 	if ((retval = cpci_unconfigure_slot(slot))) {
131 		err("%s - could not unconfigure slot %s",
132 		    __func__, slot_name(slot));
133 		goto disable_error;
134 	}
135 	dbg("%s - finished unconfiguring slot %s", __func__, slot_name(slot));
136 
137 	/* Clear EXT (by setting it) */
138 	if (cpci_clear_ext(slot)) {
139 		err("%s - could not clear EXT for slot %s",
140 		    __func__, slot_name(slot));
141 		retval = -ENODEV;
142 		goto disable_error;
143 	}
144 	cpci_led_on(slot);
145 
146 	if (controller->ops->set_power)
147 		if ((retval = controller->ops->set_power(slot, 0)))
148 			goto disable_error;
149 
150 	if (update_adapter_status(slot->hotplug_slot, 0))
151 		warn("failure to update adapter file");
152 
153 	if (slot->extracting) {
154 		slot->extracting = 0;
155 		atomic_dec(&extracting);
156 	}
157 disable_error:
158 	up_write(&list_rwsem);
159 	return retval;
160 }
161 
162 static u8
cpci_get_power_status(struct slot * slot)163 cpci_get_power_status(struct slot *slot)
164 {
165 	u8 power = 1;
166 
167 	if (controller->ops->get_power)
168 		power = controller->ops->get_power(slot);
169 	return power;
170 }
171 
172 static int
get_power_status(struct hotplug_slot * hotplug_slot,u8 * value)173 get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
174 {
175 	struct slot *slot = hotplug_slot->private;
176 
177 	*value = cpci_get_power_status(slot);
178 	return 0;
179 }
180 
181 static int
get_attention_status(struct hotplug_slot * hotplug_slot,u8 * value)182 get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
183 {
184 	struct slot *slot = hotplug_slot->private;
185 
186 	*value = cpci_get_attention_status(slot);
187 	return 0;
188 }
189 
190 static int
set_attention_status(struct hotplug_slot * hotplug_slot,u8 status)191 set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
192 {
193 	return cpci_set_attention_status(hotplug_slot->private, status);
194 }
195 
196 static int
get_adapter_status(struct hotplug_slot * hotplug_slot,u8 * value)197 get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
198 {
199 	*value = hotplug_slot->info->adapter_status;
200 	return 0;
201 }
202 
203 static int
get_latch_status(struct hotplug_slot * hotplug_slot,u8 * value)204 get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
205 {
206 	*value = hotplug_slot->info->latch_status;
207 	return 0;
208 }
209 
release_slot(struct hotplug_slot * hotplug_slot)210 static void release_slot(struct hotplug_slot *hotplug_slot)
211 {
212 	struct slot *slot = hotplug_slot->private;
213 
214 	kfree(slot->hotplug_slot->info);
215 	kfree(slot->hotplug_slot);
216 	if (slot->dev)
217 		pci_dev_put(slot->dev);
218 	kfree(slot);
219 }
220 
221 #define SLOT_NAME_SIZE	6
222 
223 int
cpci_hp_register_bus(struct pci_bus * bus,u8 first,u8 last)224 cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
225 {
226 	struct slot *slot;
227 	struct hotplug_slot *hotplug_slot;
228 	struct hotplug_slot_info *info;
229 	char name[SLOT_NAME_SIZE];
230 	int status = -ENOMEM;
231 	int i;
232 
233 	if (!(controller && bus))
234 		return -ENODEV;
235 
236 	/*
237 	 * Create a structure for each slot, and register that slot
238 	 * with the pci_hotplug subsystem.
239 	 */
240 	for (i = first; i <= last; ++i) {
241 		slot = kzalloc(sizeof (struct slot), GFP_KERNEL);
242 		if (!slot)
243 			goto error;
244 
245 		hotplug_slot =
246 			kzalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
247 		if (!hotplug_slot)
248 			goto error_slot;
249 		slot->hotplug_slot = hotplug_slot;
250 
251 		info = kzalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
252 		if (!info)
253 			goto error_hpslot;
254 		hotplug_slot->info = info;
255 
256 		slot->bus = bus;
257 		slot->number = i;
258 		slot->devfn = PCI_DEVFN(i, 0);
259 
260 		snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
261 
262 		hotplug_slot->private = slot;
263 		hotplug_slot->release = &release_slot;
264 		hotplug_slot->ops = &cpci_hotplug_slot_ops;
265 
266 		/*
267 		 * Initialize the slot info structure with some known
268 		 * good values.
269 		 */
270 		dbg("initializing slot %s", name);
271 		info->power_status = cpci_get_power_status(slot);
272 		info->attention_status = cpci_get_attention_status(slot);
273 
274 		dbg("registering slot %s", name);
275 		status = pci_hp_register(slot->hotplug_slot, bus, i, name);
276 		if (status) {
277 			err("pci_hp_register failed with error %d", status);
278 			goto error_info;
279 		}
280 		dbg("slot registered with name: %s", slot_name(slot));
281 
282 		/* Add slot to our internal list */
283 		down_write(&list_rwsem);
284 		list_add(&slot->slot_list, &slot_list);
285 		slots++;
286 		up_write(&list_rwsem);
287 	}
288 	return 0;
289 error_info:
290 	kfree(info);
291 error_hpslot:
292 	kfree(hotplug_slot);
293 error_slot:
294 	kfree(slot);
295 error:
296 	return status;
297 }
298 
299 int
cpci_hp_unregister_bus(struct pci_bus * bus)300 cpci_hp_unregister_bus(struct pci_bus *bus)
301 {
302 	struct slot *slot;
303 	struct slot *tmp;
304 	int status = 0;
305 
306 	down_write(&list_rwsem);
307 	if (!slots) {
308 		up_write(&list_rwsem);
309 		return -1;
310 	}
311 	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
312 		if (slot->bus == bus) {
313 			list_del(&slot->slot_list);
314 			slots--;
315 
316 			dbg("deregistering slot %s", slot_name(slot));
317 			status = pci_hp_deregister(slot->hotplug_slot);
318 			if (status) {
319 				err("pci_hp_deregister failed with error %d",
320 				    status);
321 				break;
322 			}
323 		}
324 	}
325 	up_write(&list_rwsem);
326 	return status;
327 }
328 
329 /* This is the interrupt mode interrupt handler */
330 static irqreturn_t
cpci_hp_intr(int irq,void * data)331 cpci_hp_intr(int irq, void *data)
332 {
333 	dbg("entered cpci_hp_intr");
334 
335 	/* Check to see if it was our interrupt */
336 	if ((controller->irq_flags & IRQF_SHARED) &&
337 	    !controller->ops->check_irq(controller->dev_id)) {
338 		dbg("exited cpci_hp_intr, not our interrupt");
339 		return IRQ_NONE;
340 	}
341 
342 	/* Disable ENUM interrupt */
343 	controller->ops->disable_irq();
344 
345 	/* Trigger processing by the event thread */
346 	wake_up_process(cpci_thread);
347 	return IRQ_HANDLED;
348 }
349 
350 /*
351  * According to PICMG 2.1 R2.0, section 6.3.2, upon
352  * initialization, the system driver shall clear the
353  * INS bits of the cold-inserted devices.
354  */
355 static int
init_slots(int clear_ins)356 init_slots(int clear_ins)
357 {
358 	struct slot *slot;
359 	struct pci_dev* dev;
360 
361 	dbg("%s - enter", __func__);
362 	down_read(&list_rwsem);
363 	if (!slots) {
364 		up_read(&list_rwsem);
365 		return -1;
366 	}
367 	list_for_each_entry(slot, &slot_list, slot_list) {
368 		dbg("%s - looking at slot %s", __func__, slot_name(slot));
369 		if (clear_ins && cpci_check_and_clear_ins(slot))
370 			dbg("%s - cleared INS for slot %s",
371 			    __func__, slot_name(slot));
372 		dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
373 		if (dev) {
374 			if (update_adapter_status(slot->hotplug_slot, 1))
375 				warn("failure to update adapter file");
376 			if (update_latch_status(slot->hotplug_slot, 1))
377 				warn("failure to update latch file");
378 			slot->dev = dev;
379 		}
380 	}
381 	up_read(&list_rwsem);
382 	dbg("%s - exit", __func__);
383 	return 0;
384 }
385 
386 static int
check_slots(void)387 check_slots(void)
388 {
389 	struct slot *slot;
390 	int extracted;
391 	int inserted;
392 	u16 hs_csr;
393 
394 	down_read(&list_rwsem);
395 	if (!slots) {
396 		up_read(&list_rwsem);
397 		err("no slots registered, shutting down");
398 		return -1;
399 	}
400 	extracted = inserted = 0;
401 	list_for_each_entry(slot, &slot_list, slot_list) {
402 		dbg("%s - looking at slot %s", __func__, slot_name(slot));
403 		if (cpci_check_and_clear_ins(slot)) {
404 			/*
405 			 * Some broken hardware (e.g. PLX 9054AB) asserts
406 			 * ENUM# twice...
407 			 */
408 			if (slot->dev) {
409 				warn("slot %s already inserted",
410 				     slot_name(slot));
411 				inserted++;
412 				continue;
413 			}
414 
415 			/* Process insertion */
416 			dbg("%s - slot %s inserted", __func__, slot_name(slot));
417 
418 			/* GSM, debug */
419 			hs_csr = cpci_get_hs_csr(slot);
420 			dbg("%s - slot %s HS_CSR (1) = %04x",
421 			    __func__, slot_name(slot), hs_csr);
422 
423 			/* Configure device */
424 			dbg("%s - configuring slot %s",
425 			    __func__, slot_name(slot));
426 			if (cpci_configure_slot(slot)) {
427 				err("%s - could not configure slot %s",
428 				    __func__, slot_name(slot));
429 				continue;
430 			}
431 			dbg("%s - finished configuring slot %s",
432 			    __func__, slot_name(slot));
433 
434 			/* GSM, debug */
435 			hs_csr = cpci_get_hs_csr(slot);
436 			dbg("%s - slot %s HS_CSR (2) = %04x",
437 			    __func__, slot_name(slot), hs_csr);
438 
439 			if (update_latch_status(slot->hotplug_slot, 1))
440 				warn("failure to update latch file");
441 
442 			if (update_adapter_status(slot->hotplug_slot, 1))
443 				warn("failure to update adapter file");
444 
445 			cpci_led_off(slot);
446 
447 			/* GSM, debug */
448 			hs_csr = cpci_get_hs_csr(slot);
449 			dbg("%s - slot %s HS_CSR (3) = %04x",
450 			    __func__, slot_name(slot), hs_csr);
451 
452 			inserted++;
453 		} else if (cpci_check_ext(slot)) {
454 			/* Process extraction request */
455 			dbg("%s - slot %s extracted",
456 			    __func__, slot_name(slot));
457 
458 			/* GSM, debug */
459 			hs_csr = cpci_get_hs_csr(slot);
460 			dbg("%s - slot %s HS_CSR = %04x",
461 			    __func__, slot_name(slot), hs_csr);
462 
463 			if (!slot->extracting) {
464 				if (update_latch_status(slot->hotplug_slot, 0)) {
465 					warn("failure to update latch file");
466 				}
467 				slot->extracting = 1;
468 				atomic_inc(&extracting);
469 			}
470 			extracted++;
471 		} else if (slot->extracting) {
472 			hs_csr = cpci_get_hs_csr(slot);
473 			if (hs_csr == 0xffff) {
474 				/*
475 				 * Hmmm, we're likely hosed at this point, should we
476 				 * bother trying to tell the driver or not?
477 				 */
478 				err("card in slot %s was improperly removed",
479 				    slot_name(slot));
480 				if (update_adapter_status(slot->hotplug_slot, 0))
481 					warn("failure to update adapter file");
482 				slot->extracting = 0;
483 				atomic_dec(&extracting);
484 			}
485 		}
486 	}
487 	up_read(&list_rwsem);
488 	dbg("inserted=%d, extracted=%d, extracting=%d",
489 	    inserted, extracted, atomic_read(&extracting));
490 	if (inserted || extracted)
491 		return extracted;
492 	else if (!atomic_read(&extracting)) {
493 		err("cannot find ENUM# source, shutting down");
494 		return -1;
495 	}
496 	return 0;
497 }
498 
499 /* This is the interrupt mode worker thread body */
500 static int
event_thread(void * data)501 event_thread(void *data)
502 {
503 	int rc;
504 
505 	dbg("%s - event thread started", __func__);
506 	while (1) {
507 		dbg("event thread sleeping");
508 		set_current_state(TASK_INTERRUPTIBLE);
509 		schedule();
510 		if (kthread_should_stop())
511 			break;
512 		do {
513 			rc = check_slots();
514 			if (rc > 0) {
515 				/* Give userspace a chance to handle extraction */
516 				msleep(500);
517 			} else if (rc < 0) {
518 				dbg("%s - error checking slots", __func__);
519 				thread_finished = 1;
520 				goto out;
521 			}
522 		} while (atomic_read(&extracting) && !kthread_should_stop());
523 		if (kthread_should_stop())
524 			break;
525 
526 		/* Re-enable ENUM# interrupt */
527 		dbg("%s - re-enabling irq", __func__);
528 		controller->ops->enable_irq();
529 	}
530  out:
531 	return 0;
532 }
533 
534 /* This is the polling mode worker thread body */
535 static int
poll_thread(void * data)536 poll_thread(void *data)
537 {
538 	int rc;
539 
540 	while (1) {
541 		if (kthread_should_stop() || signal_pending(current))
542 			break;
543 		if (controller->ops->query_enum()) {
544 			do {
545 				rc = check_slots();
546 				if (rc > 0) {
547 					/* Give userspace a chance to handle extraction */
548 					msleep(500);
549 				} else if (rc < 0) {
550 					dbg("%s - error checking slots", __func__);
551 					thread_finished = 1;
552 					goto out;
553 				}
554 			} while (atomic_read(&extracting) && !kthread_should_stop());
555 		}
556 		msleep(100);
557 	}
558  out:
559 	return 0;
560 }
561 
562 static int
cpci_start_thread(void)563 cpci_start_thread(void)
564 {
565 	if (controller->irq)
566 		cpci_thread = kthread_run(event_thread, NULL, "cpci_hp_eventd");
567 	else
568 		cpci_thread = kthread_run(poll_thread, NULL, "cpci_hp_polld");
569 	if (IS_ERR(cpci_thread)) {
570 		err("Can't start up our thread");
571 		return PTR_ERR(cpci_thread);
572 	}
573 	thread_finished = 0;
574 	return 0;
575 }
576 
577 static void
cpci_stop_thread(void)578 cpci_stop_thread(void)
579 {
580 	kthread_stop(cpci_thread);
581 	thread_finished = 1;
582 }
583 
584 int
cpci_hp_register_controller(struct cpci_hp_controller * new_controller)585 cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
586 {
587 	int status = 0;
588 
589 	if (controller)
590 		return -1;
591 	if (!(new_controller && new_controller->ops))
592 		return -EINVAL;
593 	if (new_controller->irq) {
594 		if (!(new_controller->ops->enable_irq &&
595 		     new_controller->ops->disable_irq))
596 			status = -EINVAL;
597 		if (request_irq(new_controller->irq,
598 			       cpci_hp_intr,
599 			       new_controller->irq_flags,
600 			       MY_NAME,
601 			       new_controller->dev_id)) {
602 			err("Can't get irq %d for the hotplug cPCI controller",
603 			    new_controller->irq);
604 			status = -ENODEV;
605 		}
606 		dbg("%s - acquired controller irq %d",
607 		    __func__, new_controller->irq);
608 	}
609 	if (!status)
610 		controller = new_controller;
611 	return status;
612 }
613 
614 static void
cleanup_slots(void)615 cleanup_slots(void)
616 {
617 	struct slot *slot;
618 	struct slot *tmp;
619 
620 	/*
621 	 * Unregister all of our slots with the pci_hotplug subsystem,
622 	 * and free up all memory that we had allocated.
623 	 */
624 	down_write(&list_rwsem);
625 	if (!slots)
626 		goto cleanup_null;
627 	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
628 		list_del(&slot->slot_list);
629 		pci_hp_deregister(slot->hotplug_slot);
630 	}
631 cleanup_null:
632 	up_write(&list_rwsem);
633 	return;
634 }
635 
636 int
cpci_hp_unregister_controller(struct cpci_hp_controller * old_controller)637 cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
638 {
639 	int status = 0;
640 
641 	if (controller) {
642 		if (!thread_finished)
643 			cpci_stop_thread();
644 		if (controller->irq)
645 			free_irq(controller->irq, controller->dev_id);
646 		controller = NULL;
647 		cleanup_slots();
648 	} else
649 		status = -ENODEV;
650 	return status;
651 }
652 
653 int
cpci_hp_start(void)654 cpci_hp_start(void)
655 {
656 	static int first = 1;
657 	int status;
658 
659 	dbg("%s - enter", __func__);
660 	if (!controller)
661 		return -ENODEV;
662 
663 	down_read(&list_rwsem);
664 	if (list_empty(&slot_list)) {
665 		up_read(&list_rwsem);
666 		return -ENODEV;
667 	}
668 	up_read(&list_rwsem);
669 
670 	status = init_slots(first);
671 	if (first)
672 		first = 0;
673 	if (status)
674 		return status;
675 
676 	status = cpci_start_thread();
677 	if (status)
678 		return status;
679 	dbg("%s - thread started", __func__);
680 
681 	if (controller->irq) {
682 		/* Start enum interrupt processing */
683 		dbg("%s - enabling irq", __func__);
684 		controller->ops->enable_irq();
685 	}
686 	dbg("%s - exit", __func__);
687 	return 0;
688 }
689 
690 int
cpci_hp_stop(void)691 cpci_hp_stop(void)
692 {
693 	if (!controller)
694 		return -ENODEV;
695 	if (controller->irq) {
696 		/* Stop enum interrupt processing */
697 		dbg("%s - disabling irq", __func__);
698 		controller->ops->disable_irq();
699 	}
700 	cpci_stop_thread();
701 	return 0;
702 }
703 
704 int __init
cpci_hotplug_init(int debug)705 cpci_hotplug_init(int debug)
706 {
707 	cpci_debug = debug;
708 	return 0;
709 }
710 
711 void __exit
cpci_hotplug_exit(void)712 cpci_hotplug_exit(void)
713 {
714 	/*
715 	 * Clean everything up.
716 	 */
717 	cpci_hp_stop();
718 	cpci_hp_unregister_controller(controller);
719 }
720 
721 EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
722 EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
723 EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
724 EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
725 EXPORT_SYMBOL_GPL(cpci_hp_start);
726 EXPORT_SYMBOL_GPL(cpci_hp_stop);
727