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