Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * PCI HotPlug Controller Core
4 : : *
5 : : * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
6 : : * Copyright (C) 2001-2002 IBM Corp.
7 : : *
8 : : * All rights reserved.
9 : : *
10 : : * Send feedback to <kristen.c.accardi@intel.com>
11 : : *
12 : : * Authors:
13 : : * Greg Kroah-Hartman <greg@kroah.com>
14 : : * Scott Murray <scottm@somanetworks.com>
15 : : */
16 : :
17 : : #include <linux/module.h> /* try_module_get & module_put */
18 : : #include <linux/moduleparam.h>
19 : : #include <linux/kernel.h>
20 : : #include <linux/types.h>
21 : : #include <linux/list.h>
22 : : #include <linux/kobject.h>
23 : : #include <linux/sysfs.h>
24 : : #include <linux/pagemap.h>
25 : : #include <linux/init.h>
26 : : #include <linux/mount.h>
27 : : #include <linux/namei.h>
28 : : #include <linux/mutex.h>
29 : : #include <linux/pci.h>
30 : : #include <linux/pci_hotplug.h>
31 : : #include <linux/uaccess.h>
32 : : #include "../pci.h"
33 : : #include "cpci_hotplug.h"
34 : :
35 : : #define MY_NAME "pci_hotplug"
36 : :
37 : : #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
38 : : #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
39 : : #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
40 : : #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
41 : :
42 : : /* local variables */
43 : : static bool debug;
44 : :
45 : : static LIST_HEAD(pci_hotplug_slot_list);
46 : : static DEFINE_MUTEX(pci_hp_mutex);
47 : :
48 : : /* Weee, fun with macros... */
49 : : #define GET_STATUS(name, type) \
50 : : static int get_##name(struct hotplug_slot *slot, type *value) \
51 : : { \
52 : : const struct hotplug_slot_ops *ops = slot->ops; \
53 : : int retval = 0; \
54 : : if (!try_module_get(slot->owner)) \
55 : : return -ENODEV; \
56 : : if (ops->get_##name) \
57 : : retval = ops->get_##name(slot, value); \
58 : : module_put(slot->owner); \
59 : : return retval; \
60 : : }
61 : :
62 [ # # # # ]: 0 : GET_STATUS(power_status, u8)
63 [ # # # # ]: 0 : GET_STATUS(attention_status, u8)
64 [ # # # # ]: 0 : GET_STATUS(latch_status, u8)
65 [ # # # # ]: 0 : GET_STATUS(adapter_status, u8)
66 : :
67 : 0 : static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
68 : : {
69 : 0 : int retval;
70 : 0 : u8 value;
71 : :
72 : 0 : retval = get_power_status(pci_slot->hotplug, &value);
73 [ # # ]: 0 : if (retval)
74 : 0 : return retval;
75 : :
76 : 0 : return sprintf(buf, "%d\n", value);
77 : : }
78 : :
79 : 0 : static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
80 : : size_t count)
81 : : {
82 : 0 : struct hotplug_slot *slot = pci_slot->hotplug;
83 : 0 : unsigned long lpower;
84 : 0 : u8 power;
85 : 0 : int retval = 0;
86 : :
87 : 0 : lpower = simple_strtoul(buf, NULL, 10);
88 : 0 : power = (u8)(lpower & 0xff);
89 [ # # ]: 0 : dbg("power = %d\n", power);
90 : :
91 [ # # ]: 0 : if (!try_module_get(slot->owner)) {
92 : 0 : retval = -ENODEV;
93 : 0 : goto exit;
94 : : }
95 [ # # # ]: 0 : switch (power) {
96 : 0 : case 0:
97 [ # # ]: 0 : if (slot->ops->disable_slot)
98 : 0 : retval = slot->ops->disable_slot(slot);
99 : : break;
100 : :
101 : 0 : case 1:
102 [ # # ]: 0 : if (slot->ops->enable_slot)
103 : 0 : retval = slot->ops->enable_slot(slot);
104 : : break;
105 : :
106 : 0 : default:
107 : 0 : err("Illegal value specified for power\n");
108 : 0 : retval = -EINVAL;
109 : : }
110 : 0 : module_put(slot->owner);
111 : :
112 : : exit:
113 [ # # ]: 0 : if (retval)
114 : 0 : return retval;
115 : 0 : return count;
116 : : }
117 : :
118 : : static struct pci_slot_attribute hotplug_slot_attr_power = {
119 : : .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
120 : : .show = power_read_file,
121 : : .store = power_write_file
122 : : };
123 : :
124 : 0 : static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
125 : : {
126 : 0 : int retval;
127 : 0 : u8 value;
128 : :
129 : 0 : retval = get_attention_status(pci_slot->hotplug, &value);
130 [ # # ]: 0 : if (retval)
131 : 0 : return retval;
132 : :
133 : 0 : return sprintf(buf, "%d\n", value);
134 : : }
135 : :
136 : 0 : static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
137 : : size_t count)
138 : : {
139 : 0 : struct hotplug_slot *slot = pci_slot->hotplug;
140 : 0 : const struct hotplug_slot_ops *ops = slot->ops;
141 : 0 : unsigned long lattention;
142 : 0 : u8 attention;
143 : 0 : int retval = 0;
144 : :
145 : 0 : lattention = simple_strtoul(buf, NULL, 10);
146 : 0 : attention = (u8)(lattention & 0xff);
147 [ # # ]: 0 : dbg(" - attention = %d\n", attention);
148 : :
149 [ # # ]: 0 : if (!try_module_get(slot->owner)) {
150 : 0 : retval = -ENODEV;
151 : 0 : goto exit;
152 : : }
153 [ # # ]: 0 : if (ops->set_attention_status)
154 : 0 : retval = ops->set_attention_status(slot, attention);
155 : 0 : module_put(slot->owner);
156 : :
157 : : exit:
158 [ # # ]: 0 : if (retval)
159 : 0 : return retval;
160 : 0 : return count;
161 : : }
162 : :
163 : : static struct pci_slot_attribute hotplug_slot_attr_attention = {
164 : : .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
165 : : .show = attention_read_file,
166 : : .store = attention_write_file
167 : : };
168 : :
169 : 0 : static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
170 : : {
171 : 0 : int retval;
172 : 0 : u8 value;
173 : :
174 : 0 : retval = get_latch_status(pci_slot->hotplug, &value);
175 [ # # ]: 0 : if (retval)
176 : 0 : return retval;
177 : :
178 : 0 : return sprintf(buf, "%d\n", value);
179 : : }
180 : :
181 : : static struct pci_slot_attribute hotplug_slot_attr_latch = {
182 : : .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
183 : : .show = latch_read_file,
184 : : };
185 : :
186 : 0 : static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
187 : : {
188 : 0 : int retval;
189 : 0 : u8 value;
190 : :
191 : 0 : retval = get_adapter_status(pci_slot->hotplug, &value);
192 [ # # ]: 0 : if (retval)
193 : 0 : return retval;
194 : :
195 : 0 : return sprintf(buf, "%d\n", value);
196 : : }
197 : :
198 : : static struct pci_slot_attribute hotplug_slot_attr_presence = {
199 : : .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
200 : : .show = presence_read_file,
201 : : };
202 : :
203 : 0 : static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
204 : : size_t count)
205 : : {
206 : 0 : struct hotplug_slot *slot = pci_slot->hotplug;
207 : 0 : unsigned long ltest;
208 : 0 : u32 test;
209 : 0 : int retval = 0;
210 : :
211 : 0 : ltest = simple_strtoul(buf, NULL, 10);
212 : 0 : test = (u32)(ltest & 0xffffffff);
213 [ # # ]: 0 : dbg("test = %d\n", test);
214 : :
215 [ # # ]: 0 : if (!try_module_get(slot->owner)) {
216 : 0 : retval = -ENODEV;
217 : 0 : goto exit;
218 : : }
219 [ # # ]: 0 : if (slot->ops->hardware_test)
220 : 0 : retval = slot->ops->hardware_test(slot, test);
221 : 0 : module_put(slot->owner);
222 : :
223 : : exit:
224 [ # # ]: 0 : if (retval)
225 : 0 : return retval;
226 : 0 : return count;
227 : : }
228 : :
229 : : static struct pci_slot_attribute hotplug_slot_attr_test = {
230 : : .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
231 : : .store = test_write_file
232 : : };
233 : :
234 : 2262 : static bool has_power_file(struct pci_slot *pci_slot)
235 : : {
236 : 2262 : struct hotplug_slot *slot = pci_slot->hotplug;
237 : :
238 [ - - + - : 2262 : if ((!slot) || (!slot->ops))
- - ]
239 : : return false;
240 [ - - - + : 2262 : if ((slot->ops->enable_slot) ||
- - ]
241 [ # # # # : 0 : (slot->ops->disable_slot) ||
# # ]
242 [ # # # # : 0 : (slot->ops->get_power_status))
# # ]
243 : 2262 : return true;
244 : : return false;
245 : : }
246 : :
247 : 2262 : static bool has_attention_file(struct pci_slot *pci_slot)
248 : : {
249 : 2262 : struct hotplug_slot *slot = pci_slot->hotplug;
250 : :
251 [ - - + - : 2262 : if ((!slot) || (!slot->ops))
- - ]
252 : : return false;
253 [ - - - + : 2262 : if ((slot->ops->set_attention_status) ||
- - ]
254 [ # # # # : 0 : (slot->ops->get_attention_status))
# # ]
255 : 2262 : return true;
256 : : return false;
257 : : }
258 : :
259 : 2262 : static bool has_latch_file(struct pci_slot *pci_slot)
260 : : {
261 : 2262 : struct hotplug_slot *slot = pci_slot->hotplug;
262 : :
263 [ - - + - : 2262 : if ((!slot) || (!slot->ops))
- - ]
264 : : return false;
265 [ - - + - : 2262 : if (slot->ops->get_latch_status)
- - ]
266 : 2262 : return true;
267 : : return false;
268 : : }
269 : :
270 : 2262 : static bool has_adapter_file(struct pci_slot *pci_slot)
271 : : {
272 : 2262 : struct hotplug_slot *slot = pci_slot->hotplug;
273 : :
274 [ - - + - : 2262 : if ((!slot) || (!slot->ops))
- - ]
275 : : return false;
276 [ - - + - : 2262 : if (slot->ops->get_adapter_status)
- - ]
277 : 2262 : return true;
278 : : return false;
279 : : }
280 : :
281 : 2262 : static bool has_test_file(struct pci_slot *pci_slot)
282 : : {
283 : 2262 : struct hotplug_slot *slot = pci_slot->hotplug;
284 : :
285 [ - - + - ]: 2262 : if ((!slot) || (!slot->ops))
286 : : return false;
287 [ - - - + ]: 2262 : if (slot->ops->hardware_test)
288 : 0 : return true;
289 : : return false;
290 : : }
291 : :
292 : 2262 : static int fs_add_slot(struct pci_slot *pci_slot)
293 : : {
294 : 2262 : int retval = 0;
295 : :
296 : : /* Create symbolic link to the hotplug driver module */
297 : 2262 : pci_hp_create_module_link(pci_slot);
298 : :
299 [ + - ]: 2262 : if (has_power_file(pci_slot)) {
300 : 2262 : retval = sysfs_create_file(&pci_slot->kobj,
301 : : &hotplug_slot_attr_power.attr);
302 [ - + ]: 2262 : if (retval)
303 : 0 : goto exit_power;
304 : : }
305 : :
306 [ + - ]: 2262 : if (has_attention_file(pci_slot)) {
307 : 2262 : retval = sysfs_create_file(&pci_slot->kobj,
308 : : &hotplug_slot_attr_attention.attr);
309 [ - + ]: 2262 : if (retval)
310 : 0 : goto exit_attention;
311 : : }
312 : :
313 [ + - ]: 2262 : if (has_latch_file(pci_slot)) {
314 : 2262 : retval = sysfs_create_file(&pci_slot->kobj,
315 : : &hotplug_slot_attr_latch.attr);
316 [ - + ]: 2262 : if (retval)
317 : 0 : goto exit_latch;
318 : : }
319 : :
320 [ + - ]: 2262 : if (has_adapter_file(pci_slot)) {
321 : 2262 : retval = sysfs_create_file(&pci_slot->kobj,
322 : : &hotplug_slot_attr_presence.attr);
323 [ - + ]: 2262 : if (retval)
324 : 0 : goto exit_adapter;
325 : : }
326 : :
327 [ + - ]: 2262 : if (has_test_file(pci_slot)) {
328 : 0 : retval = sysfs_create_file(&pci_slot->kobj,
329 : : &hotplug_slot_attr_test.attr);
330 [ # # ]: 0 : if (retval)
331 : 0 : goto exit_test;
332 : : }
333 : :
334 : 2262 : goto exit;
335 : :
336 : : exit_test:
337 [ # # ]: 0 : if (has_adapter_file(pci_slot))
338 : 0 : sysfs_remove_file(&pci_slot->kobj,
339 : : &hotplug_slot_attr_presence.attr);
340 : 0 : exit_adapter:
341 [ # # ]: 0 : if (has_latch_file(pci_slot))
342 : 0 : sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
343 : 0 : exit_latch:
344 [ # # ]: 0 : if (has_attention_file(pci_slot))
345 : 0 : sysfs_remove_file(&pci_slot->kobj,
346 : : &hotplug_slot_attr_attention.attr);
347 : 0 : exit_attention:
348 [ # # ]: 0 : if (has_power_file(pci_slot))
349 : 0 : sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
350 : 0 : exit_power:
351 : 0 : pci_hp_remove_module_link(pci_slot);
352 : 2262 : exit:
353 : 2262 : return retval;
354 : : }
355 : :
356 : 0 : static void fs_remove_slot(struct pci_slot *pci_slot)
357 : : {
358 [ # # ]: 0 : if (has_power_file(pci_slot))
359 : 0 : sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
360 : :
361 [ # # ]: 0 : if (has_attention_file(pci_slot))
362 : 0 : sysfs_remove_file(&pci_slot->kobj,
363 : : &hotplug_slot_attr_attention.attr);
364 : :
365 [ # # ]: 0 : if (has_latch_file(pci_slot))
366 : 0 : sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
367 : :
368 [ # # ]: 0 : if (has_adapter_file(pci_slot))
369 : 0 : sysfs_remove_file(&pci_slot->kobj,
370 : : &hotplug_slot_attr_presence.attr);
371 : :
372 [ # # ]: 0 : if (has_test_file(pci_slot))
373 : 0 : sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
374 : :
375 : 0 : pci_hp_remove_module_link(pci_slot);
376 : 0 : }
377 : :
378 : 0 : static struct hotplug_slot *get_slot_from_name(const char *name)
379 : : {
380 : 0 : struct hotplug_slot *slot;
381 : :
382 [ # # ]: 0 : list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {
383 [ # # ]: 0 : if (strcmp(hotplug_slot_name(slot), name) == 0)
384 : 0 : return slot;
385 : : }
386 : : return NULL;
387 : : }
388 : :
389 : : /**
390 : : * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
391 : : * @bus: bus this slot is on
392 : : * @slot: pointer to the &struct hotplug_slot to register
393 : : * @devnr: device number
394 : : * @name: name registered with kobject core
395 : : * @owner: caller module owner
396 : : * @mod_name: caller module name
397 : : *
398 : : * Prepares a hotplug slot for in-kernel use and immediately publishes it to
399 : : * user space in one go. Drivers may alternatively carry out the two steps
400 : : * separately by invoking pci_hp_initialize() and pci_hp_add().
401 : : *
402 : : * Returns 0 if successful, anything else for an error.
403 : : */
404 : 2262 : int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
405 : : int devnr, const char *name,
406 : : struct module *owner, const char *mod_name)
407 : : {
408 : 2262 : int result;
409 : :
410 : 2262 : result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
411 [ # # ]: 0 : if (result)
412 : 0 : return result;
413 : :
414 : 2262 : result = pci_hp_add(slot);
415 [ - + ]: 2262 : if (result)
416 : 0 : pci_hp_destroy(slot);
417 : :
418 : : return result;
419 : : }
420 : : EXPORT_SYMBOL_GPL(__pci_hp_register);
421 : :
422 : : /**
423 : : * __pci_hp_initialize - prepare hotplug slot for in-kernel use
424 : : * @slot: pointer to the &struct hotplug_slot to initialize
425 : : * @bus: bus this slot is on
426 : : * @devnr: slot number
427 : : * @name: name registered with kobject core
428 : : * @owner: caller module owner
429 : : * @mod_name: caller module name
430 : : *
431 : : * Allocate and fill in a PCI slot for use by a hotplug driver. Once this has
432 : : * been called, the driver may invoke hotplug_slot_name() to get the slot's
433 : : * unique name. The driver must be prepared to handle a ->reset_slot callback
434 : : * from this point on.
435 : : *
436 : : * Returns 0 on success or a negative int on error.
437 : : */
438 : 2262 : int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
439 : : int devnr, const char *name, struct module *owner,
440 : : const char *mod_name)
441 : : {
442 : 2262 : struct pci_slot *pci_slot;
443 : :
444 [ - - + - ]: 2262 : if (slot == NULL)
445 : : return -ENODEV;
446 [ - - + - ]: 2262 : if (slot->ops == NULL)
447 : : return -EINVAL;
448 : :
449 : 2262 : slot->owner = owner;
450 : 2262 : slot->mod_name = mod_name;
451 : :
452 : : /*
453 : : * No problems if we call this interface from both ACPI_PCI_SLOT
454 : : * driver and call it here again. If we've already created the
455 : : * pci_slot, the interface will simply bump the refcount.
456 : : */
457 : 2262 : pci_slot = pci_create_slot(bus, devnr, name, slot);
458 [ - - - + ]: 2262 : if (IS_ERR(pci_slot))
459 [ # # ]: 0 : return PTR_ERR(pci_slot);
460 : :
461 : 2262 : slot->pci_slot = pci_slot;
462 : 2262 : pci_slot->hotplug = slot;
463 : 2262 : return 0;
464 : : }
465 : : EXPORT_SYMBOL_GPL(__pci_hp_initialize);
466 : :
467 : : /**
468 : : * pci_hp_add - publish hotplug slot to user space
469 : : * @slot: pointer to the &struct hotplug_slot to publish
470 : : *
471 : : * Make a hotplug slot's sysfs interface available and inform user space of its
472 : : * addition by sending a uevent. The hotplug driver must be prepared to handle
473 : : * all &struct hotplug_slot_ops callbacks from this point on.
474 : : *
475 : : * Returns 0 on success or a negative int on error.
476 : : */
477 : 2262 : int pci_hp_add(struct hotplug_slot *slot)
478 : : {
479 : 2262 : struct pci_slot *pci_slot = slot->pci_slot;
480 : 2262 : int result;
481 : :
482 : 2262 : result = fs_add_slot(pci_slot);
483 [ + - ]: 2262 : if (result)
484 : : return result;
485 : :
486 : 2262 : kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
487 : 2262 : mutex_lock(&pci_hp_mutex);
488 : 2262 : list_add(&slot->slot_list, &pci_hotplug_slot_list);
489 : 2262 : mutex_unlock(&pci_hp_mutex);
490 [ - + ]: 2262 : dbg("Added slot %s to the list\n", hotplug_slot_name(slot));
491 : : return 0;
492 : : }
493 : : EXPORT_SYMBOL_GPL(pci_hp_add);
494 : :
495 : : /**
496 : : * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
497 : : * @slot: pointer to the &struct hotplug_slot to deregister
498 : : *
499 : : * The @slot must have been registered with the pci hotplug subsystem
500 : : * previously with a call to pci_hp_register().
501 : : *
502 : : * Returns 0 if successful, anything else for an error.
503 : : */
504 : 0 : void pci_hp_deregister(struct hotplug_slot *slot)
505 : : {
506 : 0 : pci_hp_del(slot);
507 : 0 : pci_hp_destroy(slot);
508 : 0 : }
509 : : EXPORT_SYMBOL_GPL(pci_hp_deregister);
510 : :
511 : : /**
512 : : * pci_hp_del - unpublish hotplug slot from user space
513 : : * @slot: pointer to the &struct hotplug_slot to unpublish
514 : : *
515 : : * Remove a hotplug slot's sysfs interface.
516 : : *
517 : : * Returns 0 on success or a negative int on error.
518 : : */
519 : 0 : void pci_hp_del(struct hotplug_slot *slot)
520 : : {
521 : 0 : struct hotplug_slot *temp;
522 : :
523 [ # # # # ]: 0 : if (WARN_ON(!slot))
524 : : return;
525 : :
526 : 0 : mutex_lock(&pci_hp_mutex);
527 : 0 : temp = get_slot_from_name(hotplug_slot_name(slot));
528 [ # # # # ]: 0 : if (WARN_ON(temp != slot)) {
529 : 0 : mutex_unlock(&pci_hp_mutex);
530 : 0 : return;
531 : : }
532 : :
533 : 0 : list_del(&slot->slot_list);
534 : 0 : mutex_unlock(&pci_hp_mutex);
535 [ # # ]: 0 : dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
536 : 0 : fs_remove_slot(slot->pci_slot);
537 : : }
538 : : EXPORT_SYMBOL_GPL(pci_hp_del);
539 : :
540 : : /**
541 : : * pci_hp_destroy - remove hotplug slot from in-kernel use
542 : : * @slot: pointer to the &struct hotplug_slot to destroy
543 : : *
544 : : * Destroy a PCI slot used by a hotplug driver. Once this has been called,
545 : : * the driver may no longer invoke hotplug_slot_name() to get the slot's
546 : : * unique name. The driver no longer needs to handle a ->reset_slot callback
547 : : * from this point on.
548 : : *
549 : : * Returns 0 on success or a negative int on error.
550 : : */
551 : 0 : void pci_hp_destroy(struct hotplug_slot *slot)
552 : : {
553 : 0 : struct pci_slot *pci_slot = slot->pci_slot;
554 : :
555 : 0 : slot->pci_slot = NULL;
556 : 0 : pci_slot->hotplug = NULL;
557 : 0 : pci_destroy_slot(pci_slot);
558 : 0 : }
559 : : EXPORT_SYMBOL_GPL(pci_hp_destroy);
560 : :
561 : 78 : static int __init pci_hotplug_init(void)
562 : : {
563 : 78 : int result;
564 : :
565 : 78 : result = cpci_hotplug_init(debug);
566 [ - + ]: 78 : if (result) {
567 : 0 : err("cpci_hotplug_init with error %d\n", result);
568 : 0 : return result;
569 : : }
570 : :
571 : : return result;
572 : : }
573 : : device_initcall(pci_hotplug_init);
574 : :
575 : : /*
576 : : * not really modular, but the easiest way to keep compat with existing
577 : : * bootargs behaviour is to continue using module_param here.
578 : : */
579 : : module_param(debug, bool, 0644);
580 : : MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|