1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * PCI HotPlug Controller Core
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2001-2002 Greg Kroah-Hartman ([email protected])
61da177e4SLinus Torvalds  * Copyright (C) 2001-2002 IBM Corp.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * All rights reserved.
91da177e4SLinus Torvalds  *
10fb5f4d7aSKristen Carlson Accardi  * Send feedback to <[email protected]>
111da177e4SLinus Torvalds  *
12d9b47d54SBjorn Helgaas  * Authors:
13d9b47d54SBjorn Helgaas  *   Greg Kroah-Hartman <[email protected]>
14d9b47d54SBjorn Helgaas  *   Scott Murray <[email protected]>
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
17666550a8SLukas Wunner #include <linux/module.h>
181da177e4SLinus Torvalds #include <linux/moduleparam.h>
191da177e4SLinus Torvalds #include <linux/kernel.h>
201da177e4SLinus Torvalds #include <linux/types.h>
217a54f25cSGreg Kroah-Hartman #include <linux/kobject.h>
227a54f25cSGreg Kroah-Hartman #include <linux/sysfs.h>
231da177e4SLinus Torvalds #include <linux/pagemap.h>
241da177e4SLinus Torvalds #include <linux/init.h>
251da177e4SLinus Torvalds #include <linux/mount.h>
261da177e4SLinus Torvalds #include <linux/namei.h>
271da177e4SLinus Torvalds #include <linux/pci.h>
287a54f25cSGreg Kroah-Hartman #include <linux/pci_hotplug.h>
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
30f46753c5SAlex Chiang #include "../pci.h"
31c3139ba2SBjorn Helgaas #include "cpci_hotplug.h"
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #define MY_NAME	"pci_hotplug"
341da177e4SLinus Torvalds 
3566bef8c0SHarvey Harrison #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
361da177e4SLinus Torvalds #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
371da177e4SLinus Torvalds #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
381da177e4SLinus Torvalds #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /* local variables */
4190ab5ee9SRusty Russell static bool debug;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds /* Weee, fun with macros... */
441da177e4SLinus Torvalds #define GET_STATUS(name, type)	\
451da177e4SLinus Torvalds static int get_##name(struct hotplug_slot *slot, type *value)		\
461da177e4SLinus Torvalds {									\
4781c4b5bfSLukas Wunner 	const struct hotplug_slot_ops *ops = slot->ops;			\
481da177e4SLinus Torvalds 	int retval = 0;							\
491da177e4SLinus Torvalds 	if (ops->get_##name)						\
501da177e4SLinus Torvalds 		retval = ops->get_##name(slot, value);			\
511da177e4SLinus Torvalds 	return retval;							\
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
GET_STATUS(power_status,u8)541da177e4SLinus Torvalds GET_STATUS(power_status, u8)
551da177e4SLinus Torvalds GET_STATUS(attention_status, u8)
561da177e4SLinus Torvalds GET_STATUS(latch_status, u8)
571da177e4SLinus Torvalds GET_STATUS(adapter_status, u8)
581da177e4SLinus Torvalds 
59a6ed1f4eSYijing Wang static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds 	int retval;
621da177e4SLinus Torvalds 	u8 value;
631da177e4SLinus Torvalds 
64a6ed1f4eSYijing Wang 	retval = get_power_status(pci_slot->hotplug, &value);
651da177e4SLinus Torvalds 	if (retval)
661da177e4SLinus Torvalds 		return retval;
673c78bc61SRyan Desfosses 
68f8cf6e51SKrzysztof Wilczyński 	return sysfs_emit(buf, "%d\n", value);
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
power_write_file(struct pci_slot * pci_slot,const char * buf,size_t count)71f46753c5SAlex Chiang static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
721da177e4SLinus Torvalds 				size_t count)
731da177e4SLinus Torvalds {
74f46753c5SAlex Chiang 	struct hotplug_slot *slot = pci_slot->hotplug;
751da177e4SLinus Torvalds 	unsigned long lpower;
761da177e4SLinus Torvalds 	u8 power;
771da177e4SLinus Torvalds 	int retval = 0;
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds 	lpower = simple_strtoul(buf, NULL, 10);
801da177e4SLinus Torvalds 	power = (u8)(lpower & 0xff);
811da177e4SLinus Torvalds 	dbg("power = %d\n", power);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	switch (power) {
841da177e4SLinus Torvalds 	case 0:
851da177e4SLinus Torvalds 		if (slot->ops->disable_slot)
861da177e4SLinus Torvalds 			retval = slot->ops->disable_slot(slot);
871da177e4SLinus Torvalds 		break;
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	case 1:
901da177e4SLinus Torvalds 		if (slot->ops->enable_slot)
911da177e4SLinus Torvalds 			retval = slot->ops->enable_slot(slot);
921da177e4SLinus Torvalds 		break;
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	default:
951da177e4SLinus Torvalds 		err("Illegal value specified for power\n");
961da177e4SLinus Torvalds 		retval = -EINVAL;
971da177e4SLinus Torvalds 	}
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	if (retval)
1001da177e4SLinus Torvalds 		return retval;
1011da177e4SLinus Torvalds 	return count;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
104f46753c5SAlex Chiang static struct pci_slot_attribute hotplug_slot_attr_power = {
1051da177e4SLinus Torvalds 	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
1061da177e4SLinus Torvalds 	.show = power_read_file,
1071da177e4SLinus Torvalds 	.store = power_write_file
1081da177e4SLinus Torvalds };
1091da177e4SLinus Torvalds 
attention_read_file(struct pci_slot * pci_slot,char * buf)110a6ed1f4eSYijing Wang static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds 	int retval;
1131da177e4SLinus Torvalds 	u8 value;
1141da177e4SLinus Torvalds 
115a6ed1f4eSYijing Wang 	retval = get_attention_status(pci_slot->hotplug, &value);
1161da177e4SLinus Torvalds 	if (retval)
1171da177e4SLinus Torvalds 		return retval;
1183c78bc61SRyan Desfosses 
119f8cf6e51SKrzysztof Wilczyński 	return sysfs_emit(buf, "%d\n", value);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
attention_write_file(struct pci_slot * pci_slot,const char * buf,size_t count)122a6ed1f4eSYijing Wang static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
1231da177e4SLinus Torvalds 				    size_t count)
1241da177e4SLinus Torvalds {
12581c4b5bfSLukas Wunner 	struct hotplug_slot *slot = pci_slot->hotplug;
12681c4b5bfSLukas Wunner 	const struct hotplug_slot_ops *ops = slot->ops;
1271da177e4SLinus Torvalds 	unsigned long lattention;
1281da177e4SLinus Torvalds 	u8 attention;
1291da177e4SLinus Torvalds 	int retval = 0;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	lattention = simple_strtoul(buf, NULL, 10);
1321da177e4SLinus Torvalds 	attention = (u8)(lattention & 0xff);
1331da177e4SLinus Torvalds 	dbg(" - attention = %d\n", attention);
1341da177e4SLinus Torvalds 
135f46753c5SAlex Chiang 	if (ops->set_attention_status)
13681c4b5bfSLukas Wunner 		retval = ops->set_attention_status(slot, attention);
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	if (retval)
1391da177e4SLinus Torvalds 		return retval;
1401da177e4SLinus Torvalds 	return count;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143f46753c5SAlex Chiang static struct pci_slot_attribute hotplug_slot_attr_attention = {
1441da177e4SLinus Torvalds 	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
1451da177e4SLinus Torvalds 	.show = attention_read_file,
1461da177e4SLinus Torvalds 	.store = attention_write_file
1471da177e4SLinus Torvalds };
1481da177e4SLinus Torvalds 
latch_read_file(struct pci_slot * pci_slot,char * buf)149a6ed1f4eSYijing Wang static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
1501da177e4SLinus Torvalds {
1511da177e4SLinus Torvalds 	int retval;
1521da177e4SLinus Torvalds 	u8 value;
1531da177e4SLinus Torvalds 
154a6ed1f4eSYijing Wang 	retval = get_latch_status(pci_slot->hotplug, &value);
1551da177e4SLinus Torvalds 	if (retval)
1561da177e4SLinus Torvalds 		return retval;
1573c78bc61SRyan Desfosses 
158f8cf6e51SKrzysztof Wilczyński 	return sysfs_emit(buf, "%d\n", value);
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
161f46753c5SAlex Chiang static struct pci_slot_attribute hotplug_slot_attr_latch = {
1621da177e4SLinus Torvalds 	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
1631da177e4SLinus Torvalds 	.show = latch_read_file,
1641da177e4SLinus Torvalds };
1651da177e4SLinus Torvalds 
presence_read_file(struct pci_slot * pci_slot,char * buf)166a6ed1f4eSYijing Wang static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
1671da177e4SLinus Torvalds {
1681da177e4SLinus Torvalds 	int retval;
1691da177e4SLinus Torvalds 	u8 value;
1701da177e4SLinus Torvalds 
171a6ed1f4eSYijing Wang 	retval = get_adapter_status(pci_slot->hotplug, &value);
1721da177e4SLinus Torvalds 	if (retval)
1731da177e4SLinus Torvalds 		return retval;
1743c78bc61SRyan Desfosses 
175f8cf6e51SKrzysztof Wilczyński 	return sysfs_emit(buf, "%d\n", value);
1761da177e4SLinus Torvalds }
1771da177e4SLinus Torvalds 
178f46753c5SAlex Chiang static struct pci_slot_attribute hotplug_slot_attr_presence = {
1791da177e4SLinus Torvalds 	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
1801da177e4SLinus Torvalds 	.show = presence_read_file,
1811da177e4SLinus Torvalds };
1821da177e4SLinus Torvalds 
test_write_file(struct pci_slot * pci_slot,const char * buf,size_t count)183f46753c5SAlex Chiang static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
1841da177e4SLinus Torvalds 			       size_t count)
1851da177e4SLinus Torvalds {
186f46753c5SAlex Chiang 	struct hotplug_slot *slot = pci_slot->hotplug;
1871da177e4SLinus Torvalds 	unsigned long ltest;
1881da177e4SLinus Torvalds 	u32 test;
1891da177e4SLinus Torvalds 	int retval = 0;
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	ltest = simple_strtoul(buf, NULL, 10);
1921da177e4SLinus Torvalds 	test = (u32)(ltest & 0xffffffff);
1931da177e4SLinus Torvalds 	dbg("test = %d\n", test);
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	if (slot->ops->hardware_test)
1961da177e4SLinus Torvalds 		retval = slot->ops->hardware_test(slot, test);
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	if (retval)
1991da177e4SLinus Torvalds 		return retval;
2001da177e4SLinus Torvalds 	return count;
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
203f46753c5SAlex Chiang static struct pci_slot_attribute hotplug_slot_attr_test = {
2041da177e4SLinus Torvalds 	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
2051da177e4SLinus Torvalds 	.store = test_write_file
2061da177e4SLinus Torvalds };
2071da177e4SLinus Torvalds 
has_power_file(struct hotplug_slot * slot)20834bd6141SLukas Wunner static bool has_power_file(struct hotplug_slot *slot)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds 	if ((slot->ops->enable_slot) ||
2111da177e4SLinus Torvalds 	    (slot->ops->disable_slot) ||
2121da177e4SLinus Torvalds 	    (slot->ops->get_power_status))
213498a8fafSKenji Kaneshige 		return true;
214498a8fafSKenji Kaneshige 	return false;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
has_attention_file(struct hotplug_slot * slot)21734bd6141SLukas Wunner static bool has_attention_file(struct hotplug_slot *slot)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	if ((slot->ops->set_attention_status) ||
2201da177e4SLinus Torvalds 	    (slot->ops->get_attention_status))
221498a8fafSKenji Kaneshige 		return true;
222498a8fafSKenji Kaneshige 	return false;
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
has_latch_file(struct hotplug_slot * slot)22534bd6141SLukas Wunner static bool has_latch_file(struct hotplug_slot *slot)
2261da177e4SLinus Torvalds {
2271da177e4SLinus Torvalds 	if (slot->ops->get_latch_status)
228498a8fafSKenji Kaneshige 		return true;
229498a8fafSKenji Kaneshige 	return false;
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
has_adapter_file(struct hotplug_slot * slot)23234bd6141SLukas Wunner static bool has_adapter_file(struct hotplug_slot *slot)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	if (slot->ops->get_adapter_status)
235498a8fafSKenji Kaneshige 		return true;
236498a8fafSKenji Kaneshige 	return false;
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
has_test_file(struct hotplug_slot * slot)23934bd6141SLukas Wunner static bool has_test_file(struct hotplug_slot *slot)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds 	if (slot->ops->hardware_test)
242498a8fafSKenji Kaneshige 		return true;
243498a8fafSKenji Kaneshige 	return false;
2441da177e4SLinus Torvalds }
2451da177e4SLinus Torvalds 
fs_add_slot(struct hotplug_slot * slot,struct pci_slot * pci_slot)24634bd6141SLukas Wunner static int fs_add_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot)
2471da177e4SLinus Torvalds {
248*cc973ef1SLukas Wunner 	struct kobject *kobj;
249660a0e8fSGreg Kroah-Hartman 	int retval = 0;
2501da177e4SLinus Torvalds 
251c825bc94SKenji Kaneshige 	/* Create symbolic link to the hotplug driver module */
252*cc973ef1SLukas Wunner 	kobj = kset_find_obj(module_kset, slot->mod_name);
253*cc973ef1SLukas Wunner 	if (kobj) {
254*cc973ef1SLukas Wunner 		retval = sysfs_create_link(&pci_slot->kobj, kobj, "module");
255*cc973ef1SLukas Wunner 		if (retval)
256*cc973ef1SLukas Wunner 			dev_err(&pci_slot->bus->dev,
257*cc973ef1SLukas Wunner 				"Error creating sysfs link (%d)\n", retval);
258*cc973ef1SLukas Wunner 		kobject_put(kobj);
259*cc973ef1SLukas Wunner 	}
260c825bc94SKenji Kaneshige 
26134bd6141SLukas Wunner 	if (has_power_file(slot)) {
262a6ed1f4eSYijing Wang 		retval = sysfs_create_file(&pci_slot->kobj,
263498a8fafSKenji Kaneshige 					   &hotplug_slot_attr_power.attr);
264660a0e8fSGreg Kroah-Hartman 		if (retval)
265660a0e8fSGreg Kroah-Hartman 			goto exit_power;
266660a0e8fSGreg Kroah-Hartman 	}
2671da177e4SLinus Torvalds 
26834bd6141SLukas Wunner 	if (has_attention_file(slot)) {
269a6ed1f4eSYijing Wang 		retval = sysfs_create_file(&pci_slot->kobj,
270660a0e8fSGreg Kroah-Hartman 					   &hotplug_slot_attr_attention.attr);
271660a0e8fSGreg Kroah-Hartman 		if (retval)
272660a0e8fSGreg Kroah-Hartman 			goto exit_attention;
273660a0e8fSGreg Kroah-Hartman 	}
2741da177e4SLinus Torvalds 
27534bd6141SLukas Wunner 	if (has_latch_file(slot)) {
276a6ed1f4eSYijing Wang 		retval = sysfs_create_file(&pci_slot->kobj,
277660a0e8fSGreg Kroah-Hartman 					   &hotplug_slot_attr_latch.attr);
278660a0e8fSGreg Kroah-Hartman 		if (retval)
279660a0e8fSGreg Kroah-Hartman 			goto exit_latch;
280660a0e8fSGreg Kroah-Hartman 	}
2811da177e4SLinus Torvalds 
28234bd6141SLukas Wunner 	if (has_adapter_file(slot)) {
283a6ed1f4eSYijing Wang 		retval = sysfs_create_file(&pci_slot->kobj,
284660a0e8fSGreg Kroah-Hartman 					   &hotplug_slot_attr_presence.attr);
285660a0e8fSGreg Kroah-Hartman 		if (retval)
286660a0e8fSGreg Kroah-Hartman 			goto exit_adapter;
287660a0e8fSGreg Kroah-Hartman 	}
2881da177e4SLinus Torvalds 
28934bd6141SLukas Wunner 	if (has_test_file(slot)) {
290a6ed1f4eSYijing Wang 		retval = sysfs_create_file(&pci_slot->kobj,
291660a0e8fSGreg Kroah-Hartman 					   &hotplug_slot_attr_test.attr);
292660a0e8fSGreg Kroah-Hartman 		if (retval)
293660a0e8fSGreg Kroah-Hartman 			goto exit_test;
294660a0e8fSGreg Kroah-Hartman 	}
295660a0e8fSGreg Kroah-Hartman 
296660a0e8fSGreg Kroah-Hartman 	goto exit;
297660a0e8fSGreg Kroah-Hartman 
298660a0e8fSGreg Kroah-Hartman exit_test:
29934bd6141SLukas Wunner 	if (has_adapter_file(slot))
300a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj,
301498a8fafSKenji Kaneshige 				  &hotplug_slot_attr_presence.attr);
302660a0e8fSGreg Kroah-Hartman exit_adapter:
30334bd6141SLukas Wunner 	if (has_latch_file(slot))
304a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
305660a0e8fSGreg Kroah-Hartman exit_latch:
30634bd6141SLukas Wunner 	if (has_attention_file(slot))
307a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj,
308498a8fafSKenji Kaneshige 				  &hotplug_slot_attr_attention.attr);
309660a0e8fSGreg Kroah-Hartman exit_attention:
31034bd6141SLukas Wunner 	if (has_power_file(slot))
311a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
312660a0e8fSGreg Kroah-Hartman exit_power:
313*cc973ef1SLukas Wunner 	sysfs_remove_link(&pci_slot->kobj, "module");
314660a0e8fSGreg Kroah-Hartman exit:
315660a0e8fSGreg Kroah-Hartman 	return retval;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
fs_remove_slot(struct hotplug_slot * slot,struct pci_slot * pci_slot)31834bd6141SLukas Wunner static void fs_remove_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot)
3191da177e4SLinus Torvalds {
32034bd6141SLukas Wunner 	if (has_power_file(slot))
321a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
3221da177e4SLinus Torvalds 
32334bd6141SLukas Wunner 	if (has_attention_file(slot))
324a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj,
325498a8fafSKenji Kaneshige 				  &hotplug_slot_attr_attention.attr);
3261da177e4SLinus Torvalds 
32734bd6141SLukas Wunner 	if (has_latch_file(slot))
328a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
3291da177e4SLinus Torvalds 
33034bd6141SLukas Wunner 	if (has_adapter_file(slot))
331a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj,
332498a8fafSKenji Kaneshige 				  &hotplug_slot_attr_presence.attr);
3331da177e4SLinus Torvalds 
33434bd6141SLukas Wunner 	if (has_test_file(slot))
335a6ed1f4eSYijing Wang 		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
336c825bc94SKenji Kaneshige 
337*cc973ef1SLukas Wunner 	sysfs_remove_link(&pci_slot->kobj, "module");
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds /**
341c825bc94SKenji Kaneshige  * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
3421da177e4SLinus Torvalds  * @slot: pointer to the &struct hotplug_slot to register
343b21a18f7SJulia Lawall  * @bus: bus this slot is on
344c825bc94SKenji Kaneshige  * @devnr: device number
3451359f270SAlex Chiang  * @name: name registered with kobject core
346503998caSRandy Dunlap  * @owner: caller module owner
347503998caSRandy Dunlap  * @mod_name: caller module name
3481da177e4SLinus Torvalds  *
34951bbf9beSLukas Wunner  * Prepares a hotplug slot for in-kernel use and immediately publishes it to
35051bbf9beSLukas Wunner  * user space in one go.  Drivers may alternatively carry out the two steps
35151bbf9beSLukas Wunner  * separately by invoking pci_hp_initialize() and pci_hp_add().
3521da177e4SLinus Torvalds  *
3531da177e4SLinus Torvalds  * Returns 0 if successful, anything else for an error.
3541da177e4SLinus Torvalds  */
__pci_hp_register(struct hotplug_slot * slot,struct pci_bus * bus,int devnr,const char * name,struct module * owner,const char * mod_name)355c825bc94SKenji Kaneshige int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
356c825bc94SKenji Kaneshige 		      int devnr, const char *name,
357c825bc94SKenji Kaneshige 		      struct module *owner, const char *mod_name)
3581da177e4SLinus Torvalds {
3591da177e4SLinus Torvalds 	int result;
36051bbf9beSLukas Wunner 
36151bbf9beSLukas Wunner 	result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
36251bbf9beSLukas Wunner 	if (result)
36351bbf9beSLukas Wunner 		return result;
36451bbf9beSLukas Wunner 
36551bbf9beSLukas Wunner 	result = pci_hp_add(slot);
36651bbf9beSLukas Wunner 	if (result)
36751bbf9beSLukas Wunner 		pci_hp_destroy(slot);
36851bbf9beSLukas Wunner 
36951bbf9beSLukas Wunner 	return result;
37051bbf9beSLukas Wunner }
37151bbf9beSLukas Wunner EXPORT_SYMBOL_GPL(__pci_hp_register);
37251bbf9beSLukas Wunner 
37351bbf9beSLukas Wunner /**
37451bbf9beSLukas Wunner  * __pci_hp_initialize - prepare hotplug slot for in-kernel use
37551bbf9beSLukas Wunner  * @slot: pointer to the &struct hotplug_slot to initialize
37651bbf9beSLukas Wunner  * @bus: bus this slot is on
37751bbf9beSLukas Wunner  * @devnr: slot number
37851bbf9beSLukas Wunner  * @name: name registered with kobject core
37951bbf9beSLukas Wunner  * @owner: caller module owner
38051bbf9beSLukas Wunner  * @mod_name: caller module name
38151bbf9beSLukas Wunner  *
38251bbf9beSLukas Wunner  * Allocate and fill in a PCI slot for use by a hotplug driver.  Once this has
38351bbf9beSLukas Wunner  * been called, the driver may invoke hotplug_slot_name() to get the slot's
38451bbf9beSLukas Wunner  * unique name.  The driver must be prepared to handle a ->reset_slot callback
38551bbf9beSLukas Wunner  * from this point on.
38651bbf9beSLukas Wunner  *
38751bbf9beSLukas Wunner  * Returns 0 on success or a negative int on error.
38851bbf9beSLukas Wunner  */
__pci_hp_initialize(struct hotplug_slot * slot,struct pci_bus * bus,int devnr,const char * name,struct module * owner,const char * mod_name)38951bbf9beSLukas Wunner int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
39051bbf9beSLukas Wunner 			int devnr, const char *name, struct module *owner,
39151bbf9beSLukas Wunner 			const char *mod_name)
39251bbf9beSLukas Wunner {
393f46753c5SAlex Chiang 	struct pci_slot *pci_slot;
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 	if (slot == NULL)
3961da177e4SLinus Torvalds 		return -ENODEV;
397a7da2161SLukas Wunner 	if (slot->ops == NULL)
3981da177e4SLinus Torvalds 		return -EINVAL;
3991da177e4SLinus Torvalds 
40081c4b5bfSLukas Wunner 	slot->owner = owner;
40181c4b5bfSLukas Wunner 	slot->mod_name = mod_name;
40295cb9093SKenji Kaneshige 
4038344b568SAlex Chiang 	/*
4048344b568SAlex Chiang 	 * No problems if we call this interface from both ACPI_PCI_SLOT
4058344b568SAlex Chiang 	 * driver and call it here again. If we've already created the
4068344b568SAlex Chiang 	 * pci_slot, the interface will simply bump the refcount.
4078344b568SAlex Chiang 	 */
408c825bc94SKenji Kaneshige 	pci_slot = pci_create_slot(bus, devnr, name, slot);
40951bbf9beSLukas Wunner 	if (IS_ERR(pci_slot))
41051bbf9beSLukas Wunner 		return PTR_ERR(pci_slot);
4111da177e4SLinus Torvalds 
412f46753c5SAlex Chiang 	slot->pci_slot = pci_slot;
413f46753c5SAlex Chiang 	pci_slot->hotplug = slot;
41451bbf9beSLukas Wunner 	return 0;
41551bbf9beSLukas Wunner }
41651bbf9beSLukas Wunner EXPORT_SYMBOL_GPL(__pci_hp_initialize);
4171da177e4SLinus Torvalds 
41851bbf9beSLukas Wunner /**
41951bbf9beSLukas Wunner  * pci_hp_add - publish hotplug slot to user space
42051bbf9beSLukas Wunner  * @slot: pointer to the &struct hotplug_slot to publish
42151bbf9beSLukas Wunner  *
42251bbf9beSLukas Wunner  * Make a hotplug slot's sysfs interface available and inform user space of its
42351bbf9beSLukas Wunner  * addition by sending a uevent.  The hotplug driver must be prepared to handle
42451bbf9beSLukas Wunner  * all &struct hotplug_slot_ops callbacks from this point on.
42551bbf9beSLukas Wunner  *
42651bbf9beSLukas Wunner  * Returns 0 on success or a negative int on error.
42751bbf9beSLukas Wunner  */
pci_hp_add(struct hotplug_slot * slot)42851bbf9beSLukas Wunner int pci_hp_add(struct hotplug_slot *slot)
42951bbf9beSLukas Wunner {
43062460bcbSLukas Wunner 	struct pci_slot *pci_slot;
43151bbf9beSLukas Wunner 	int result;
432f46753c5SAlex Chiang 
43362460bcbSLukas Wunner 	if (WARN_ON(!slot))
43462460bcbSLukas Wunner 		return -EINVAL;
43562460bcbSLukas Wunner 
43662460bcbSLukas Wunner 	pci_slot = slot->pci_slot;
43762460bcbSLukas Wunner 
43834bd6141SLukas Wunner 	result = fs_add_slot(slot, pci_slot);
4394ce64358SLukas Wunner 	if (result)
44051bbf9beSLukas Wunner 		return result;
4414ce64358SLukas Wunner 
442f46753c5SAlex Chiang 	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
44351bbf9beSLukas Wunner 	return 0;
4441da177e4SLinus Torvalds }
44551bbf9beSLukas Wunner EXPORT_SYMBOL_GPL(pci_hp_add);
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds /**
4481da177e4SLinus Torvalds  * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
449a6ed1f4eSYijing Wang  * @slot: pointer to the &struct hotplug_slot to deregister
4501da177e4SLinus Torvalds  *
4511da177e4SLinus Torvalds  * The @slot must have been registered with the pci hotplug subsystem
4521da177e4SLinus Torvalds  * previously with a call to pci_hp_register().
4531da177e4SLinus Torvalds  */
pci_hp_deregister(struct hotplug_slot * slot)45451bbf9beSLukas Wunner void pci_hp_deregister(struct hotplug_slot *slot)
45551bbf9beSLukas Wunner {
45651bbf9beSLukas Wunner 	pci_hp_del(slot);
45751bbf9beSLukas Wunner 	pci_hp_destroy(slot);
45851bbf9beSLukas Wunner }
45951bbf9beSLukas Wunner EXPORT_SYMBOL_GPL(pci_hp_deregister);
46051bbf9beSLukas Wunner 
46151bbf9beSLukas Wunner /**
46251bbf9beSLukas Wunner  * pci_hp_del - unpublish hotplug slot from user space
46351bbf9beSLukas Wunner  * @slot: pointer to the &struct hotplug_slot to unpublish
46451bbf9beSLukas Wunner  *
46551bbf9beSLukas Wunner  * Remove a hotplug slot's sysfs interface.
46651bbf9beSLukas Wunner  */
pci_hp_del(struct hotplug_slot * slot)46751bbf9beSLukas Wunner void pci_hp_del(struct hotplug_slot *slot)
4681da177e4SLinus Torvalds {
46951bbf9beSLukas Wunner 	if (WARN_ON(!slot))
47051bbf9beSLukas Wunner 		return;
4711da177e4SLinus Torvalds 
47234bd6141SLukas Wunner 	fs_remove_slot(slot, slot->pci_slot);
47351bbf9beSLukas Wunner }
47451bbf9beSLukas Wunner EXPORT_SYMBOL_GPL(pci_hp_del);
475f46753c5SAlex Chiang 
47651bbf9beSLukas Wunner /**
47751bbf9beSLukas Wunner  * pci_hp_destroy - remove hotplug slot from in-kernel use
47851bbf9beSLukas Wunner  * @slot: pointer to the &struct hotplug_slot to destroy
47951bbf9beSLukas Wunner  *
48051bbf9beSLukas Wunner  * Destroy a PCI slot used by a hotplug driver.  Once this has been called,
48151bbf9beSLukas Wunner  * the driver may no longer invoke hotplug_slot_name() to get the slot's
48251bbf9beSLukas Wunner  * unique name.  The driver no longer needs to handle a ->reset_slot callback
48351bbf9beSLukas Wunner  * from this point on.
48451bbf9beSLukas Wunner  */
pci_hp_destroy(struct hotplug_slot * slot)48551bbf9beSLukas Wunner void pci_hp_destroy(struct hotplug_slot *slot)
48651bbf9beSLukas Wunner {
48751bbf9beSLukas Wunner 	struct pci_slot *pci_slot = slot->pci_slot;
48851bbf9beSLukas Wunner 
48951bbf9beSLukas Wunner 	slot->pci_slot = NULL;
490a6ed1f4eSYijing Wang 	pci_slot->hotplug = NULL;
491a6ed1f4eSYijing Wang 	pci_destroy_slot(pci_slot);
4921da177e4SLinus Torvalds }
49351bbf9beSLukas Wunner EXPORT_SYMBOL_GPL(pci_hp_destroy);
4941da177e4SLinus Torvalds 
pci_hotplug_init(void)4951da177e4SLinus Torvalds static int __init pci_hotplug_init(void)
4961da177e4SLinus Torvalds {
4971da177e4SLinus Torvalds 	int result;
4980fed80f7SGreg Kroah-Hartman 
4991da177e4SLinus Torvalds 	result = cpci_hotplug_init(debug);
5001da177e4SLinus Torvalds 	if (result) {
5011da177e4SLinus Torvalds 		err("cpci_hotplug_init with error %d\n", result);
5023c78bc61SRyan Desfosses 		return result;
5031da177e4SLinus Torvalds 	}
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	return result;
5061da177e4SLinus Torvalds }
50757b51b9aSPaul Gortmaker device_initcall(pci_hotplug_init);
5081da177e4SLinus Torvalds 
50957b51b9aSPaul Gortmaker /*
51057b51b9aSPaul Gortmaker  * not really modular, but the easiest way to keep compat with existing
51157b51b9aSPaul Gortmaker  * bootargs behaviour is to continue using module_param here.
51257b51b9aSPaul Gortmaker  */
5131da177e4SLinus Torvalds module_param(debug, bool, 0644);
5141da177e4SLinus Torvalds MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
515