1/*
2 * PCI HotPlug Controller Core
3 *
4 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
5 * Copyright (C) 2001-2002 IBM Corp.
6 *
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
17 * NON INFRINGEMENT.  See the GNU General Public License for more
18 * details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 * Send feedback to <kristen.c.accardi@intel.com>
25 *
26 */
27
28#include <linux/module.h>
29#include <linux/moduleparam.h>
30#include <linux/kernel.h>
31#include <linux/types.h>
32#include <linux/list.h>
33#include <linux/kobject.h>
34#include <linux/sysfs.h>
35#include <linux/pagemap.h>
36#include <linux/slab.h>
37#include <linux/init.h>
38#include <linux/mount.h>
39#include <linux/namei.h>
40#include <linux/pci.h>
41#include <linux/pci_hotplug.h>
42#include <asm/uaccess.h>
43
44#define MY_NAME	"pci_hotplug"
45
46#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
47#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
48#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
49#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
50
51
52/* local variables */
53static int debug;
54
55#define DRIVER_VERSION	"0.5"
56#define DRIVER_AUTHOR	"Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
57#define DRIVER_DESC	"PCI Hot Plug PCI Core"
58
59
60//////////////////////////////////////////////////////////////////
61
62static LIST_HEAD(pci_hotplug_slot_list);
63
64struct kset pci_hotplug_slots_subsys;
65
66static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
67		struct attribute *attr, char *buf)
68{
69	struct hotplug_slot *slot = to_hotplug_slot(kobj);
70	struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
71	return attribute->show ? attribute->show(slot, buf) : -EIO;
72}
73
74static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
75		struct attribute *attr, const char *buf, size_t len)
76{
77	struct hotplug_slot *slot = to_hotplug_slot(kobj);
78	struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
79	return attribute->store ? attribute->store(slot, buf, len) : -EIO;
80}
81
82static struct sysfs_ops hotplug_slot_sysfs_ops = {
83	.show = hotplug_slot_attr_show,
84	.store = hotplug_slot_attr_store,
85};
86
87static void hotplug_slot_release(struct kobject *kobj)
88{
89	struct hotplug_slot *slot = to_hotplug_slot(kobj);
90	if (slot->release)
91		slot->release(slot);
92}
93
94static struct kobj_type hotplug_slot_ktype = {
95	.sysfs_ops = &hotplug_slot_sysfs_ops,
96	.release = &hotplug_slot_release,
97};
98
99decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
100
101/* these strings match up with the values in pci_bus_speed */
102static char *pci_bus_speed_strings[] = {
103	"33 MHz PCI",		/* 0x00 */
104	"66 MHz PCI",		/* 0x01 */
105	"66 MHz PCIX", 		/* 0x02 */
106	"100 MHz PCIX",		/* 0x03 */
107	"133 MHz PCIX",		/* 0x04 */
108	NULL,			/* 0x05 */
109	NULL,			/* 0x06 */
110	NULL,			/* 0x07 */
111	NULL,			/* 0x08 */
112	"66 MHz PCIX 266",	/* 0x09 */
113	"100 MHz PCIX 266",	/* 0x0a */
114	"133 MHz PCIX 266",	/* 0x0b */
115	NULL,			/* 0x0c */
116	NULL,			/* 0x0d */
117	NULL,			/* 0x0e */
118	NULL,			/* 0x0f */
119	NULL,			/* 0x10 */
120	"66 MHz PCIX 533",	/* 0x11 */
121	"100 MHz PCIX 533",	/* 0x12 */
122	"133 MHz PCIX 533",	/* 0x13 */
123	"25 GBps PCI-E",	/* 0x14 */
124};
125
126#ifdef CONFIG_HOTPLUG_PCI_CPCI
127extern int cpci_hotplug_init(int debug);
128extern void cpci_hotplug_exit(void);
129#else
130static inline int cpci_hotplug_init(int debug) { return 0; }
131static inline void cpci_hotplug_exit(void) { }
132#endif
133
134/* Weee, fun with macros... */
135#define GET_STATUS(name,type)	\
136static int get_##name (struct hotplug_slot *slot, type *value)		\
137{									\
138	struct hotplug_slot_ops *ops = slot->ops;			\
139	int retval = 0;							\
140	if (try_module_get(ops->owner)) {				\
141		if (ops->get_##name)					\
142			retval = ops->get_##name (slot, value);		\
143		else							\
144			*value = slot->info->name;			\
145		module_put(ops->owner);					\
146	}								\
147	return retval;							\
148}
149
150GET_STATUS(power_status, u8)
151GET_STATUS(attention_status, u8)
152GET_STATUS(latch_status, u8)
153GET_STATUS(adapter_status, u8)
154GET_STATUS(address, u32)
155GET_STATUS(max_bus_speed, enum pci_bus_speed)
156GET_STATUS(cur_bus_speed, enum pci_bus_speed)
157
158static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
159{
160	int retval;
161	u8 value;
162
163	retval = get_power_status (slot, &value);
164	if (retval)
165		goto exit;
166	retval = sprintf (buf, "%d\n", value);
167exit:
168	return retval;
169}
170
171static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
172		size_t count)
173{
174	unsigned long lpower;
175	u8 power;
176	int retval = 0;
177
178	lpower = simple_strtoul (buf, NULL, 10);
179	power = (u8)(lpower & 0xff);
180	dbg ("power = %d\n", power);
181
182	if (!try_module_get(slot->ops->owner)) {
183		retval = -ENODEV;
184		goto exit;
185	}
186	switch (power) {
187		case 0:
188			if (slot->ops->disable_slot)
189				retval = slot->ops->disable_slot(slot);
190			break;
191
192		case 1:
193			if (slot->ops->enable_slot)
194				retval = slot->ops->enable_slot(slot);
195			break;
196
197		default:
198			err ("Illegal value specified for power\n");
199			retval = -EINVAL;
200	}
201	module_put(slot->ops->owner);
202
203exit:
204	if (retval)
205		return retval;
206	return count;
207}
208
209static struct hotplug_slot_attribute hotplug_slot_attr_power = {
210	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
211	.show = power_read_file,
212	.store = power_write_file
213};
214
215static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
216{
217	int retval;
218	u8 value;
219
220	retval = get_attention_status (slot, &value);
221	if (retval)
222		goto exit;
223	retval = sprintf (buf, "%d\n", value);
224
225exit:
226	return retval;
227}
228
229static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
230		size_t count)
231{
232	unsigned long lattention;
233	u8 attention;
234	int retval = 0;
235
236	lattention = simple_strtoul (buf, NULL, 10);
237	attention = (u8)(lattention & 0xff);
238	dbg (" - attention = %d\n", attention);
239
240	if (!try_module_get(slot->ops->owner)) {
241		retval = -ENODEV;
242		goto exit;
243	}
244	if (slot->ops->set_attention_status)
245		retval = slot->ops->set_attention_status(slot, attention);
246	module_put(slot->ops->owner);
247
248exit:
249	if (retval)
250		return retval;
251	return count;
252}
253
254static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
255	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
256	.show = attention_read_file,
257	.store = attention_write_file
258};
259
260static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
261{
262	int retval;
263	u8 value;
264
265	retval = get_latch_status (slot, &value);
266	if (retval)
267		goto exit;
268	retval = sprintf (buf, "%d\n", value);
269
270exit:
271	return retval;
272}
273
274static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
275	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
276	.show = latch_read_file,
277};
278
279static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
280{
281	int retval;
282	u8 value;
283
284	retval = get_adapter_status (slot, &value);
285	if (retval)
286		goto exit;
287	retval = sprintf (buf, "%d\n", value);
288
289exit:
290	return retval;
291}
292
293static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
294	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
295	.show = presence_read_file,
296};
297
298static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
299{
300	int retval;
301	u32 address;
302
303	retval = get_address (slot, &address);
304	if (retval)
305		goto exit;
306	retval = sprintf (buf, "%04x:%02x:%02x\n",
307			  (address >> 16) & 0xffff,
308			  (address >> 8) & 0xff,
309			  address & 0xff);
310
311exit:
312	return retval;
313}
314
315static struct hotplug_slot_attribute hotplug_slot_attr_address = {
316	.attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
317	.show = address_read_file,
318};
319
320static char *unknown_speed = "Unknown bus speed";
321
322static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
323{
324	char *speed_string;
325	int retval;
326	enum pci_bus_speed value;
327
328	retval = get_max_bus_speed (slot, &value);
329	if (retval)
330		goto exit;
331
332	if (value == PCI_SPEED_UNKNOWN)
333		speed_string = unknown_speed;
334	else
335		speed_string = pci_bus_speed_strings[value];
336
337	retval = sprintf (buf, "%s\n", speed_string);
338
339exit:
340	return retval;
341}
342
343static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
344	.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
345	.show = max_bus_speed_read_file,
346};
347
348static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
349{
350	char *speed_string;
351	int retval;
352	enum pci_bus_speed value;
353
354	retval = get_cur_bus_speed (slot, &value);
355	if (retval)
356		goto exit;
357
358	if (value == PCI_SPEED_UNKNOWN)
359		speed_string = unknown_speed;
360	else
361		speed_string = pci_bus_speed_strings[value];
362
363	retval = sprintf (buf, "%s\n", speed_string);
364
365exit:
366	return retval;
367}
368
369static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
370	.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
371	.show = cur_bus_speed_read_file,
372};
373
374static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
375		size_t count)
376{
377	unsigned long ltest;
378	u32 test;
379	int retval = 0;
380
381	ltest = simple_strtoul (buf, NULL, 10);
382	test = (u32)(ltest & 0xffffffff);
383	dbg ("test = %d\n", test);
384
385	if (!try_module_get(slot->ops->owner)) {
386		retval = -ENODEV;
387		goto exit;
388	}
389	if (slot->ops->hardware_test)
390		retval = slot->ops->hardware_test(slot, test);
391	module_put(slot->ops->owner);
392
393exit:
394	if (retval)
395		return retval;
396	return count;
397}
398
399static struct hotplug_slot_attribute hotplug_slot_attr_test = {
400	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
401	.store = test_write_file
402};
403
404static int has_power_file (struct hotplug_slot *slot)
405{
406	if ((!slot) || (!slot->ops))
407		return -ENODEV;
408	if ((slot->ops->enable_slot) ||
409	    (slot->ops->disable_slot) ||
410	    (slot->ops->get_power_status))
411		return 0;
412	return -ENOENT;
413}
414
415static int has_attention_file (struct hotplug_slot *slot)
416{
417	if ((!slot) || (!slot->ops))
418		return -ENODEV;
419	if ((slot->ops->set_attention_status) ||
420	    (slot->ops->get_attention_status))
421		return 0;
422	return -ENOENT;
423}
424
425static int has_latch_file (struct hotplug_slot *slot)
426{
427	if ((!slot) || (!slot->ops))
428		return -ENODEV;
429	if (slot->ops->get_latch_status)
430		return 0;
431	return -ENOENT;
432}
433
434static int has_adapter_file (struct hotplug_slot *slot)
435{
436	if ((!slot) || (!slot->ops))
437		return -ENODEV;
438	if (slot->ops->get_adapter_status)
439		return 0;
440	return -ENOENT;
441}
442
443static int has_address_file (struct hotplug_slot *slot)
444{
445	if ((!slot) || (!slot->ops))
446		return -ENODEV;
447	if (slot->ops->get_address)
448		return 0;
449	return -ENOENT;
450}
451
452static int has_max_bus_speed_file (struct hotplug_slot *slot)
453{
454	if ((!slot) || (!slot->ops))
455		return -ENODEV;
456	if (slot->ops->get_max_bus_speed)
457		return 0;
458	return -ENOENT;
459}
460
461static int has_cur_bus_speed_file (struct hotplug_slot *slot)
462{
463	if ((!slot) || (!slot->ops))
464		return -ENODEV;
465	if (slot->ops->get_cur_bus_speed)
466		return 0;
467	return -ENOENT;
468}
469
470static int has_test_file (struct hotplug_slot *slot)
471{
472	if ((!slot) || (!slot->ops))
473		return -ENODEV;
474	if (slot->ops->hardware_test)
475		return 0;
476	return -ENOENT;
477}
478
479static int fs_add_slot (struct hotplug_slot *slot)
480{
481	int retval = 0;
482
483	if (has_power_file(slot) == 0) {
484		retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
485		if (retval)
486			goto exit_power;
487	}
488
489	if (has_attention_file(slot) == 0) {
490		retval = sysfs_create_file(&slot->kobj,
491					   &hotplug_slot_attr_attention.attr);
492		if (retval)
493			goto exit_attention;
494	}
495
496	if (has_latch_file(slot) == 0) {
497		retval = sysfs_create_file(&slot->kobj,
498					   &hotplug_slot_attr_latch.attr);
499		if (retval)
500			goto exit_latch;
501	}
502
503	if (has_adapter_file(slot) == 0) {
504		retval = sysfs_create_file(&slot->kobj,
505					   &hotplug_slot_attr_presence.attr);
506		if (retval)
507			goto exit_adapter;
508	}
509
510	if (has_address_file(slot) == 0) {
511		retval = sysfs_create_file(&slot->kobj,
512					   &hotplug_slot_attr_address.attr);
513		if (retval)
514			goto exit_address;
515	}
516
517	if (has_max_bus_speed_file(slot) == 0) {
518		retval = sysfs_create_file(&slot->kobj,
519					   &hotplug_slot_attr_max_bus_speed.attr);
520		if (retval)
521			goto exit_max_speed;
522	}
523
524	if (has_cur_bus_speed_file(slot) == 0) {
525		retval = sysfs_create_file(&slot->kobj,
526					   &hotplug_slot_attr_cur_bus_speed.attr);
527		if (retval)
528			goto exit_cur_speed;
529	}
530
531	if (has_test_file(slot) == 0) {
532		retval = sysfs_create_file(&slot->kobj,
533					   &hotplug_slot_attr_test.attr);
534		if (retval)
535			goto exit_test;
536	}
537
538	goto exit;
539
540exit_test:
541	if (has_cur_bus_speed_file(slot) == 0)
542		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
543
544exit_cur_speed:
545	if (has_max_bus_speed_file(slot) == 0)
546		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
547
548exit_max_speed:
549	if (has_address_file(slot) == 0)
550		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
551
552exit_address:
553	if (has_adapter_file(slot) == 0)
554		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
555
556exit_adapter:
557	if (has_latch_file(slot) == 0)
558		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
559
560exit_latch:
561	if (has_attention_file(slot) == 0)
562		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
563
564exit_attention:
565	if (has_power_file(slot) == 0)
566		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
567exit_power:
568exit:
569	return retval;
570}
571
572static void fs_remove_slot (struct hotplug_slot *slot)
573{
574	if (has_power_file(slot) == 0)
575		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
576
577	if (has_attention_file(slot) == 0)
578		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
579
580	if (has_latch_file(slot) == 0)
581		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
582
583	if (has_adapter_file(slot) == 0)
584		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
585
586	if (has_address_file(slot) == 0)
587		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
588
589	if (has_max_bus_speed_file(slot) == 0)
590		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
591
592	if (has_cur_bus_speed_file(slot) == 0)
593		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
594
595	if (has_test_file(slot) == 0)
596		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
597}
598
599static struct hotplug_slot *get_slot_from_name (const char *name)
600{
601	struct hotplug_slot *slot;
602	struct list_head *tmp;
603
604	list_for_each (tmp, &pci_hotplug_slot_list) {
605		slot = list_entry (tmp, struct hotplug_slot, slot_list);
606		if (strcmp(slot->name, name) == 0)
607			return slot;
608	}
609	return NULL;
610}
611
612/**
613 * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
614 * @slot: pointer to the &struct hotplug_slot to register
615 *
616 * Registers a hotplug slot with the pci hotplug subsystem, which will allow
617 * userspace interaction to the slot.
618 *
619 * Returns 0 if successful, anything else for an error.
620 */
621int pci_hp_register (struct hotplug_slot *slot)
622{
623	int result;
624
625	if (slot == NULL)
626		return -ENODEV;
627	if ((slot->info == NULL) || (slot->ops == NULL))
628		return -EINVAL;
629	if (slot->release == NULL) {
630		dbg("Why are you trying to register a hotplug slot"
631		    "without a proper release function?\n");
632		return -EINVAL;
633	}
634
635	kobject_set_name(&slot->kobj, "%s", slot->name);
636	kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
637
638	/* this can fail if we have already registered a slot with the same name */
639	if (kobject_register(&slot->kobj)) {
640		err("Unable to register kobject");
641		return -EINVAL;
642	}
643
644	list_add (&slot->slot_list, &pci_hotplug_slot_list);
645
646	result = fs_add_slot (slot);
647	dbg ("Added slot %s to the list\n", slot->name);
648	return result;
649}
650
651/**
652 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
653 * @slot: pointer to the &struct hotplug_slot to deregister
654 *
655 * The @slot must have been registered with the pci hotplug subsystem
656 * previously with a call to pci_hp_register().
657 *
658 * Returns 0 if successful, anything else for an error.
659 */
660int pci_hp_deregister (struct hotplug_slot *slot)
661{
662	struct hotplug_slot *temp;
663
664	if (slot == NULL)
665		return -ENODEV;
666
667	temp = get_slot_from_name (slot->name);
668	if (temp != slot) {
669		return -ENODEV;
670	}
671	list_del (&slot->slot_list);
672
673	fs_remove_slot (slot);
674	dbg ("Removed slot %s from the list\n", slot->name);
675	kobject_unregister(&slot->kobj);
676	return 0;
677}
678
679/**
680 * pci_hp_change_slot_info - changes the slot's information structure in the core
681 * @slot: pointer to the slot whose info has changed
682 * @info: pointer to the info copy into the slot's info structure
683 *
684 * @slot must have been registered with the pci
685 * hotplug subsystem previously with a call to pci_hp_register().
686 *
687 * Returns 0 if successful, anything else for an error.
688 */
689int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
690					 struct hotplug_slot_info *info)
691{
692	int retval;
693
694	if ((slot == NULL) || (info == NULL))
695		return -ENODEV;
696
697	/*
698	* check all fields in the info structure, and update timestamps
699	* for the files referring to the fields that have now changed.
700	*/
701	if ((has_power_file(slot) == 0) &&
702	    (slot->info->power_status != info->power_status)) {
703		retval = sysfs_update_file(&slot->kobj,
704					   &hotplug_slot_attr_power.attr);
705		if (retval)
706			return retval;
707	}
708
709	if ((has_attention_file(slot) == 0) &&
710	    (slot->info->attention_status != info->attention_status)) {
711		retval = sysfs_update_file(&slot->kobj,
712					   &hotplug_slot_attr_attention.attr);
713		if (retval)
714			return retval;
715	}
716
717	if ((has_latch_file(slot) == 0) &&
718	    (slot->info->latch_status != info->latch_status)) {
719		retval = sysfs_update_file(&slot->kobj,
720					   &hotplug_slot_attr_latch.attr);
721		if (retval)
722			return retval;
723	}
724
725	if ((has_adapter_file(slot) == 0) &&
726	    (slot->info->adapter_status != info->adapter_status)) {
727		retval = sysfs_update_file(&slot->kobj,
728					   &hotplug_slot_attr_presence.attr);
729		if (retval)
730			return retval;
731	}
732
733	if ((has_address_file(slot) == 0) &&
734	    (slot->info->address != info->address)) {
735		retval = sysfs_update_file(&slot->kobj,
736					   &hotplug_slot_attr_address.attr);
737		if (retval)
738			return retval;
739	}
740
741	if ((has_max_bus_speed_file(slot) == 0) &&
742	    (slot->info->max_bus_speed != info->max_bus_speed)) {
743		retval = sysfs_update_file(&slot->kobj,
744					   &hotplug_slot_attr_max_bus_speed.attr);
745		if (retval)
746			return retval;
747	}
748
749	if ((has_cur_bus_speed_file(slot) == 0) &&
750	    (slot->info->cur_bus_speed != info->cur_bus_speed)) {
751		retval = sysfs_update_file(&slot->kobj,
752					   &hotplug_slot_attr_cur_bus_speed.attr);
753		if (retval)
754			return retval;
755	}
756
757	memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
758
759	return 0;
760}
761
762static int __init pci_hotplug_init (void)
763{
764	int result;
765
766	kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
767	result = subsystem_register(&pci_hotplug_slots_subsys);
768	if (result) {
769		err("Register subsys with error %d\n", result);
770		goto exit;
771	}
772	result = cpci_hotplug_init(debug);
773	if (result) {
774		err ("cpci_hotplug_init with error %d\n", result);
775		goto err_subsys;
776	}
777
778	info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
779	goto exit;
780
781err_subsys:
782	subsystem_unregister(&pci_hotplug_slots_subsys);
783exit:
784	return result;
785}
786
787static void __exit pci_hotplug_exit (void)
788{
789	cpci_hotplug_exit();
790	subsystem_unregister(&pci_hotplug_slots_subsys);
791}
792
793module_init(pci_hotplug_init);
794module_exit(pci_hotplug_exit);
795
796MODULE_AUTHOR(DRIVER_AUTHOR);
797MODULE_DESCRIPTION(DRIVER_DESC);
798MODULE_LICENSE("GPL");
799module_param(debug, bool, 0644);
800MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
801
802EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
803EXPORT_SYMBOL_GPL(pci_hp_register);
804EXPORT_SYMBOL_GPL(pci_hp_deregister);
805EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
806