1 // SPDX-License-Identifier: GPL-2.0+
3 * PCI HotPlug Controller Core
5 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
6 * Copyright (C) 2001-2002 IBM Corp.
10 * Send feedback to <kristen.c.accardi@intel.com>
13 * Greg Kroah-Hartman <greg@kroah.com>
14 * Scott Murray <scottm@somanetworks.com>
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>
33 #include "cpci_hotplug.h"
35 #define MY_NAME "pci_hotplug"
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)
45 static LIST_HEAD(pci_hotplug_slot_list
);
46 static DEFINE_MUTEX(pci_hp_mutex
);
48 /* Weee, fun with macros... */
49 #define GET_STATUS(name, type) \
50 static int get_##name(struct hotplug_slot *slot, type *value) \
52 struct hotplug_slot_ops *ops = slot->ops; \
54 if (!try_module_get(ops->owner)) \
56 if (ops->get_##name) \
57 retval = ops->get_##name(slot, value); \
59 *value = slot->info->name; \
60 module_put(ops->owner); \
64 GET_STATUS(power_status
, u8
)
65 GET_STATUS(attention_status
, u8
)
66 GET_STATUS(latch_status
, u8
)
67 GET_STATUS(adapter_status
, u8
)
69 static ssize_t
power_read_file(struct pci_slot
*pci_slot
, char *buf
)
74 retval
= get_power_status(pci_slot
->hotplug
, &value
);
78 return sprintf(buf
, "%d\n", value
);
81 static ssize_t
power_write_file(struct pci_slot
*pci_slot
, const char *buf
,
84 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
89 lpower
= simple_strtoul(buf
, NULL
, 10);
90 power
= (u8
)(lpower
& 0xff);
91 dbg("power = %d\n", power
);
93 if (!try_module_get(slot
->ops
->owner
)) {
99 if (slot
->ops
->disable_slot
)
100 retval
= slot
->ops
->disable_slot(slot
);
104 if (slot
->ops
->enable_slot
)
105 retval
= slot
->ops
->enable_slot(slot
);
109 err("Illegal value specified for power\n");
112 module_put(slot
->ops
->owner
);
120 static struct pci_slot_attribute hotplug_slot_attr_power
= {
121 .attr
= {.name
= "power", .mode
= S_IFREG
| S_IRUGO
| S_IWUSR
},
122 .show
= power_read_file
,
123 .store
= power_write_file
126 static ssize_t
attention_read_file(struct pci_slot
*pci_slot
, char *buf
)
131 retval
= get_attention_status(pci_slot
->hotplug
, &value
);
135 return sprintf(buf
, "%d\n", value
);
138 static ssize_t
attention_write_file(struct pci_slot
*pci_slot
, const char *buf
,
141 struct hotplug_slot_ops
*ops
= pci_slot
->hotplug
->ops
;
142 unsigned long lattention
;
146 lattention
= simple_strtoul(buf
, NULL
, 10);
147 attention
= (u8
)(lattention
& 0xff);
148 dbg(" - attention = %d\n", attention
);
150 if (!try_module_get(ops
->owner
)) {
154 if (ops
->set_attention_status
)
155 retval
= ops
->set_attention_status(pci_slot
->hotplug
, attention
);
156 module_put(ops
->owner
);
164 static struct pci_slot_attribute hotplug_slot_attr_attention
= {
165 .attr
= {.name
= "attention", .mode
= S_IFREG
| S_IRUGO
| S_IWUSR
},
166 .show
= attention_read_file
,
167 .store
= attention_write_file
170 static ssize_t
latch_read_file(struct pci_slot
*pci_slot
, char *buf
)
175 retval
= get_latch_status(pci_slot
->hotplug
, &value
);
179 return sprintf(buf
, "%d\n", value
);
182 static struct pci_slot_attribute hotplug_slot_attr_latch
= {
183 .attr
= {.name
= "latch", .mode
= S_IFREG
| S_IRUGO
},
184 .show
= latch_read_file
,
187 static ssize_t
presence_read_file(struct pci_slot
*pci_slot
, char *buf
)
192 retval
= get_adapter_status(pci_slot
->hotplug
, &value
);
196 return sprintf(buf
, "%d\n", value
);
199 static struct pci_slot_attribute hotplug_slot_attr_presence
= {
200 .attr
= {.name
= "adapter", .mode
= S_IFREG
| S_IRUGO
},
201 .show
= presence_read_file
,
204 static ssize_t
test_write_file(struct pci_slot
*pci_slot
, const char *buf
,
207 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
212 ltest
= simple_strtoul(buf
, NULL
, 10);
213 test
= (u32
)(ltest
& 0xffffffff);
214 dbg("test = %d\n", test
);
216 if (!try_module_get(slot
->ops
->owner
)) {
220 if (slot
->ops
->hardware_test
)
221 retval
= slot
->ops
->hardware_test(slot
, test
);
222 module_put(slot
->ops
->owner
);
230 static struct pci_slot_attribute hotplug_slot_attr_test
= {
231 .attr
= {.name
= "test", .mode
= S_IFREG
| S_IRUGO
| S_IWUSR
},
232 .store
= test_write_file
235 static bool has_power_file(struct pci_slot
*pci_slot
)
237 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
239 if ((!slot
) || (!slot
->ops
))
241 if ((slot
->ops
->enable_slot
) ||
242 (slot
->ops
->disable_slot
) ||
243 (slot
->ops
->get_power_status
))
248 static bool has_attention_file(struct pci_slot
*pci_slot
)
250 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
252 if ((!slot
) || (!slot
->ops
))
254 if ((slot
->ops
->set_attention_status
) ||
255 (slot
->ops
->get_attention_status
))
260 static bool has_latch_file(struct pci_slot
*pci_slot
)
262 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
264 if ((!slot
) || (!slot
->ops
))
266 if (slot
->ops
->get_latch_status
)
271 static bool has_adapter_file(struct pci_slot
*pci_slot
)
273 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
275 if ((!slot
) || (!slot
->ops
))
277 if (slot
->ops
->get_adapter_status
)
282 static bool has_test_file(struct pci_slot
*pci_slot
)
284 struct hotplug_slot
*slot
= pci_slot
->hotplug
;
286 if ((!slot
) || (!slot
->ops
))
288 if (slot
->ops
->hardware_test
)
293 static int fs_add_slot(struct pci_slot
*pci_slot
)
297 /* Create symbolic link to the hotplug driver module */
298 pci_hp_create_module_link(pci_slot
);
300 if (has_power_file(pci_slot
)) {
301 retval
= sysfs_create_file(&pci_slot
->kobj
,
302 &hotplug_slot_attr_power
.attr
);
307 if (has_attention_file(pci_slot
)) {
308 retval
= sysfs_create_file(&pci_slot
->kobj
,
309 &hotplug_slot_attr_attention
.attr
);
314 if (has_latch_file(pci_slot
)) {
315 retval
= sysfs_create_file(&pci_slot
->kobj
,
316 &hotplug_slot_attr_latch
.attr
);
321 if (has_adapter_file(pci_slot
)) {
322 retval
= sysfs_create_file(&pci_slot
->kobj
,
323 &hotplug_slot_attr_presence
.attr
);
328 if (has_test_file(pci_slot
)) {
329 retval
= sysfs_create_file(&pci_slot
->kobj
,
330 &hotplug_slot_attr_test
.attr
);
338 if (has_adapter_file(pci_slot
))
339 sysfs_remove_file(&pci_slot
->kobj
,
340 &hotplug_slot_attr_presence
.attr
);
342 if (has_latch_file(pci_slot
))
343 sysfs_remove_file(&pci_slot
->kobj
, &hotplug_slot_attr_latch
.attr
);
345 if (has_attention_file(pci_slot
))
346 sysfs_remove_file(&pci_slot
->kobj
,
347 &hotplug_slot_attr_attention
.attr
);
349 if (has_power_file(pci_slot
))
350 sysfs_remove_file(&pci_slot
->kobj
, &hotplug_slot_attr_power
.attr
);
352 pci_hp_remove_module_link(pci_slot
);
357 static void fs_remove_slot(struct pci_slot
*pci_slot
)
359 if (has_power_file(pci_slot
))
360 sysfs_remove_file(&pci_slot
->kobj
, &hotplug_slot_attr_power
.attr
);
362 if (has_attention_file(pci_slot
))
363 sysfs_remove_file(&pci_slot
->kobj
,
364 &hotplug_slot_attr_attention
.attr
);
366 if (has_latch_file(pci_slot
))
367 sysfs_remove_file(&pci_slot
->kobj
, &hotplug_slot_attr_latch
.attr
);
369 if (has_adapter_file(pci_slot
))
370 sysfs_remove_file(&pci_slot
->kobj
,
371 &hotplug_slot_attr_presence
.attr
);
373 if (has_test_file(pci_slot
))
374 sysfs_remove_file(&pci_slot
->kobj
, &hotplug_slot_attr_test
.attr
);
376 pci_hp_remove_module_link(pci_slot
);
379 static struct hotplug_slot
*get_slot_from_name(const char *name
)
381 struct hotplug_slot
*slot
;
383 list_for_each_entry(slot
, &pci_hotplug_slot_list
, slot_list
) {
384 if (strcmp(hotplug_slot_name(slot
), name
) == 0)
391 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
392 * @bus: bus this slot is on
393 * @slot: pointer to the &struct hotplug_slot to register
394 * @devnr: device number
395 * @name: name registered with kobject core
396 * @owner: caller module owner
397 * @mod_name: caller module name
399 * Registers a hotplug slot with the pci hotplug subsystem, which will allow
400 * userspace interaction to the slot.
402 * Returns 0 if successful, anything else for an error.
404 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
)
409 struct pci_slot
*pci_slot
;
413 if ((slot
->info
== NULL
) || (slot
->ops
== NULL
))
415 if (slot
->release
== NULL
) {
416 dbg("Why are you trying to register a hotplug slot without a proper release function?\n");
420 slot
->ops
->owner
= owner
;
421 slot
->ops
->mod_name
= mod_name
;
423 mutex_lock(&pci_hp_mutex
);
425 * No problems if we call this interface from both ACPI_PCI_SLOT
426 * driver and call it here again. If we've already created the
427 * pci_slot, the interface will simply bump the refcount.
429 pci_slot
= pci_create_slot(bus
, devnr
, name
, slot
);
430 if (IS_ERR(pci_slot
)) {
431 result
= PTR_ERR(pci_slot
);
435 slot
->pci_slot
= pci_slot
;
436 pci_slot
->hotplug
= slot
;
438 list_add(&slot
->slot_list
, &pci_hotplug_slot_list
);
440 result
= fs_add_slot(pci_slot
);
441 kobject_uevent(&pci_slot
->kobj
, KOBJ_ADD
);
442 dbg("Added slot %s to the list\n", name
);
444 mutex_unlock(&pci_hp_mutex
);
447 EXPORT_SYMBOL_GPL(__pci_hp_register
);
450 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
451 * @slot: pointer to the &struct hotplug_slot to deregister
453 * The @slot must have been registered with the pci hotplug subsystem
454 * previously with a call to pci_hp_register().
456 * Returns 0 if successful, anything else for an error.
458 int pci_hp_deregister(struct hotplug_slot
*slot
)
460 struct hotplug_slot
*temp
;
461 struct pci_slot
*pci_slot
;
466 mutex_lock(&pci_hp_mutex
);
467 temp
= get_slot_from_name(hotplug_slot_name(slot
));
469 mutex_unlock(&pci_hp_mutex
);
473 list_del(&slot
->slot_list
);
475 pci_slot
= slot
->pci_slot
;
476 fs_remove_slot(pci_slot
);
477 dbg("Removed slot %s from the list\n", hotplug_slot_name(slot
));
480 pci_slot
->hotplug
= NULL
;
481 pci_destroy_slot(pci_slot
);
482 mutex_unlock(&pci_hp_mutex
);
486 EXPORT_SYMBOL_GPL(pci_hp_deregister
);
489 * pci_hp_change_slot_info - changes the slot's information structure in the core
490 * @slot: pointer to the slot whose info has changed
491 * @info: pointer to the info copy into the slot's info structure
493 * @slot must have been registered with the pci
494 * hotplug subsystem previously with a call to pci_hp_register().
496 * Returns 0 if successful, anything else for an error.
498 int pci_hp_change_slot_info(struct hotplug_slot
*slot
,
499 struct hotplug_slot_info
*info
)
504 memcpy(slot
->info
, info
, sizeof(struct hotplug_slot_info
));
508 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info
);
510 static int __init
pci_hotplug_init(void)
514 result
= cpci_hotplug_init(debug
);
516 err("cpci_hotplug_init with error %d\n", result
);
522 device_initcall(pci_hotplug_init
);
525 * not really modular, but the easiest way to keep compat with existing
526 * bootargs behaviour is to continue using module_param here.
528 module_param(debug
, bool, 0644);
529 MODULE_PARM_DESC(debug
, "Debugging mode enabled or not");