Linux-libre 2.6.32.3-gnu1
[librecmc/linux-libre.git] / drivers / pci / hotplug / pci_hotplug_core.c
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/mutex.h>
41 #include <linux/pci.h>
42 #include <linux/pci_hotplug.h>
43 #include <asm/uaccess.h>
44 #include "../pci.h"
45
46 #define MY_NAME "pci_hotplug"
47
48 #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
49 #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
50 #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
51 #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
52
53
54 /* local variables */
55 static int debug;
56
57 #define DRIVER_VERSION  "0.5"
58 #define DRIVER_AUTHOR   "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
59 #define DRIVER_DESC     "PCI Hot Plug PCI Core"
60
61
62 //////////////////////////////////////////////////////////////////
63
64 static LIST_HEAD(pci_hotplug_slot_list);
65 static DEFINE_MUTEX(pci_hp_mutex);
66
67 /* these strings match up with the values in pci_bus_speed */
68 static char *pci_bus_speed_strings[] = {
69         "33 MHz PCI",           /* 0x00 */
70         "66 MHz PCI",           /* 0x01 */
71         "66 MHz PCIX",          /* 0x02 */
72         "100 MHz PCIX",         /* 0x03 */
73         "133 MHz PCIX",         /* 0x04 */
74         NULL,                   /* 0x05 */
75         NULL,                   /* 0x06 */
76         NULL,                   /* 0x07 */
77         NULL,                   /* 0x08 */
78         "66 MHz PCIX 266",      /* 0x09 */
79         "100 MHz PCIX 266",     /* 0x0a */
80         "133 MHz PCIX 266",     /* 0x0b */
81         NULL,                   /* 0x0c */
82         NULL,                   /* 0x0d */
83         NULL,                   /* 0x0e */
84         NULL,                   /* 0x0f */
85         NULL,                   /* 0x10 */
86         "66 MHz PCIX 533",      /* 0x11 */
87         "100 MHz PCIX 533",     /* 0x12 */
88         "133 MHz PCIX 533",     /* 0x13 */
89         "2.5 GT/s PCI-E",       /* 0x14 */
90         "5.0 GT/s PCI-E",       /* 0x15 */
91 };
92
93 #ifdef CONFIG_HOTPLUG_PCI_CPCI
94 extern int cpci_hotplug_init(int debug);
95 extern void cpci_hotplug_exit(void);
96 #else
97 static inline int cpci_hotplug_init(int debug) { return 0; }
98 static inline void cpci_hotplug_exit(void) { }
99 #endif
100
101 /* Weee, fun with macros... */
102 #define GET_STATUS(name,type)   \
103 static int get_##name (struct hotplug_slot *slot, type *value)          \
104 {                                                                       \
105         struct hotplug_slot_ops *ops = slot->ops;                       \
106         int retval = 0;                                                 \
107         if (!try_module_get(ops->owner))                                \
108                 return -ENODEV;                                         \
109         if (ops->get_##name)                                            \
110                 retval = ops->get_##name(slot, value);                  \
111         else                                                            \
112                 *value = slot->info->name;                              \
113         module_put(ops->owner);                                         \
114         return retval;                                                  \
115 }
116
117 GET_STATUS(power_status, u8)
118 GET_STATUS(attention_status, u8)
119 GET_STATUS(latch_status, u8)
120 GET_STATUS(adapter_status, u8)
121 GET_STATUS(max_bus_speed, enum pci_bus_speed)
122 GET_STATUS(cur_bus_speed, enum pci_bus_speed)
123
124 static ssize_t power_read_file(struct pci_slot *slot, char *buf)
125 {
126         int retval;
127         u8 value;
128
129         retval = get_power_status(slot->hotplug, &value);
130         if (retval)
131                 goto exit;
132         retval = sprintf (buf, "%d\n", value);
133 exit:
134         return retval;
135 }
136
137 static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
138                 size_t count)
139 {
140         struct hotplug_slot *slot = pci_slot->hotplug;
141         unsigned long lpower;
142         u8 power;
143         int retval = 0;
144
145         lpower = simple_strtoul (buf, NULL, 10);
146         power = (u8)(lpower & 0xff);
147         dbg ("power = %d\n", power);
148
149         if (!try_module_get(slot->ops->owner)) {
150                 retval = -ENODEV;
151                 goto exit;
152         }
153         switch (power) {
154                 case 0:
155                         if (slot->ops->disable_slot)
156                                 retval = slot->ops->disable_slot(slot);
157                         break;
158
159                 case 1:
160                         if (slot->ops->enable_slot)
161                                 retval = slot->ops->enable_slot(slot);
162                         break;
163
164                 default:
165                         err ("Illegal value specified for power\n");
166                         retval = -EINVAL;
167         }
168         module_put(slot->ops->owner);
169
170 exit:   
171         if (retval)
172                 return retval;
173         return count;
174 }
175
176 static struct pci_slot_attribute hotplug_slot_attr_power = {
177         .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
178         .show = power_read_file,
179         .store = power_write_file
180 };
181
182 static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
183 {
184         int retval;
185         u8 value;
186
187         retval = get_attention_status(slot->hotplug, &value);
188         if (retval)
189                 goto exit;
190         retval = sprintf(buf, "%d\n", value);
191
192 exit:
193         return retval;
194 }
195
196 static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
197                 size_t count)
198 {
199         struct hotplug_slot_ops *ops = slot->hotplug->ops;
200         unsigned long lattention;
201         u8 attention;
202         int retval = 0;
203
204         lattention = simple_strtoul (buf, NULL, 10);
205         attention = (u8)(lattention & 0xff);
206         dbg (" - attention = %d\n", attention);
207
208         if (!try_module_get(ops->owner)) {
209                 retval = -ENODEV;
210                 goto exit;
211         }
212         if (ops->set_attention_status)
213                 retval = ops->set_attention_status(slot->hotplug, attention);
214         module_put(ops->owner);
215
216 exit:   
217         if (retval)
218                 return retval;
219         return count;
220 }
221
222 static struct pci_slot_attribute hotplug_slot_attr_attention = {
223         .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
224         .show = attention_read_file,
225         .store = attention_write_file
226 };
227
228 static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
229 {
230         int retval;
231         u8 value;
232
233         retval = get_latch_status(slot->hotplug, &value);
234         if (retval)
235                 goto exit;
236         retval = sprintf (buf, "%d\n", value);
237
238 exit:
239         return retval;
240 }
241
242 static struct pci_slot_attribute hotplug_slot_attr_latch = {
243         .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
244         .show = latch_read_file,
245 };
246
247 static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
248 {
249         int retval;
250         u8 value;
251
252         retval = get_adapter_status(slot->hotplug, &value);
253         if (retval)
254                 goto exit;
255         retval = sprintf (buf, "%d\n", value);
256
257 exit:
258         return retval;
259 }
260
261 static struct pci_slot_attribute hotplug_slot_attr_presence = {
262         .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
263         .show = presence_read_file,
264 };
265
266 static char *unknown_speed = "Unknown bus speed";
267
268 static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
269 {
270         char *speed_string;
271         int retval;
272         enum pci_bus_speed value;
273         
274         retval = get_max_bus_speed(slot->hotplug, &value);
275         if (retval)
276                 goto exit;
277
278         if (value == PCI_SPEED_UNKNOWN)
279                 speed_string = unknown_speed;
280         else
281                 speed_string = pci_bus_speed_strings[value];
282         
283         retval = sprintf (buf, "%s\n", speed_string);
284
285 exit:
286         return retval;
287 }
288
289 static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
290         .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
291         .show = max_bus_speed_read_file,
292 };
293
294 static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
295 {
296         char *speed_string;
297         int retval;
298         enum pci_bus_speed value;
299
300         retval = get_cur_bus_speed(slot->hotplug, &value);
301         if (retval)
302                 goto exit;
303
304         if (value == PCI_SPEED_UNKNOWN)
305                 speed_string = unknown_speed;
306         else
307                 speed_string = pci_bus_speed_strings[value];
308         
309         retval = sprintf (buf, "%s\n", speed_string);
310
311 exit:
312         return retval;
313 }
314
315 static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
316         .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
317         .show = cur_bus_speed_read_file,
318 };
319
320 static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
321                 size_t count)
322 {
323         struct hotplug_slot *slot = pci_slot->hotplug;
324         unsigned long ltest;
325         u32 test;
326         int retval = 0;
327
328         ltest = simple_strtoul (buf, NULL, 10);
329         test = (u32)(ltest & 0xffffffff);
330         dbg ("test = %d\n", test);
331
332         if (!try_module_get(slot->ops->owner)) {
333                 retval = -ENODEV;
334                 goto exit;
335         }
336         if (slot->ops->hardware_test)
337                 retval = slot->ops->hardware_test(slot, test);
338         module_put(slot->ops->owner);
339
340 exit:   
341         if (retval)
342                 return retval;
343         return count;
344 }
345
346 static struct pci_slot_attribute hotplug_slot_attr_test = {
347         .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
348         .store = test_write_file
349 };
350
351 static bool has_power_file(struct pci_slot *pci_slot)
352 {
353         struct hotplug_slot *slot = pci_slot->hotplug;
354         if ((!slot) || (!slot->ops))
355                 return false;
356         if ((slot->ops->enable_slot) ||
357             (slot->ops->disable_slot) ||
358             (slot->ops->get_power_status))
359                 return true;
360         return false;
361 }
362
363 static bool has_attention_file(struct pci_slot *pci_slot)
364 {
365         struct hotplug_slot *slot = pci_slot->hotplug;
366         if ((!slot) || (!slot->ops))
367                 return false;
368         if ((slot->ops->set_attention_status) ||
369             (slot->ops->get_attention_status))
370                 return true;
371         return false;
372 }
373
374 static bool has_latch_file(struct pci_slot *pci_slot)
375 {
376         struct hotplug_slot *slot = pci_slot->hotplug;
377         if ((!slot) || (!slot->ops))
378                 return false;
379         if (slot->ops->get_latch_status)
380                 return true;
381         return false;
382 }
383
384 static bool has_adapter_file(struct pci_slot *pci_slot)
385 {
386         struct hotplug_slot *slot = pci_slot->hotplug;
387         if ((!slot) || (!slot->ops))
388                 return false;
389         if (slot->ops->get_adapter_status)
390                 return true;
391         return false;
392 }
393
394 static bool has_max_bus_speed_file(struct pci_slot *pci_slot)
395 {
396         struct hotplug_slot *slot = pci_slot->hotplug;
397         if ((!slot) || (!slot->ops))
398                 return false;
399         if (slot->ops->get_max_bus_speed)
400                 return true;
401         return false;
402 }
403
404 static bool has_cur_bus_speed_file(struct pci_slot *pci_slot)
405 {
406         struct hotplug_slot *slot = pci_slot->hotplug;
407         if ((!slot) || (!slot->ops))
408                 return false;
409         if (slot->ops->get_cur_bus_speed)
410                 return true;
411         return false;
412 }
413
414 static bool has_test_file(struct pci_slot *pci_slot)
415 {
416         struct hotplug_slot *slot = pci_slot->hotplug;
417         if ((!slot) || (!slot->ops))
418                 return false;
419         if (slot->ops->hardware_test)
420                 return true;
421         return false;
422 }
423
424 static int fs_add_slot(struct pci_slot *slot)
425 {
426         int retval = 0;
427
428         /* Create symbolic link to the hotplug driver module */
429         pci_hp_create_module_link(slot);
430
431         if (has_power_file(slot)) {
432                 retval = sysfs_create_file(&slot->kobj,
433                                            &hotplug_slot_attr_power.attr);
434                 if (retval)
435                         goto exit_power;
436         }
437
438         if (has_attention_file(slot)) {
439                 retval = sysfs_create_file(&slot->kobj,
440                                            &hotplug_slot_attr_attention.attr);
441                 if (retval)
442                         goto exit_attention;
443         }
444
445         if (has_latch_file(slot)) {
446                 retval = sysfs_create_file(&slot->kobj,
447                                            &hotplug_slot_attr_latch.attr);
448                 if (retval)
449                         goto exit_latch;
450         }
451
452         if (has_adapter_file(slot)) {
453                 retval = sysfs_create_file(&slot->kobj,
454                                            &hotplug_slot_attr_presence.attr);
455                 if (retval)
456                         goto exit_adapter;
457         }
458
459         if (has_max_bus_speed_file(slot)) {
460                 retval = sysfs_create_file(&slot->kobj,
461                                         &hotplug_slot_attr_max_bus_speed.attr);
462                 if (retval)
463                         goto exit_max_speed;
464         }
465
466         if (has_cur_bus_speed_file(slot)) {
467                 retval = sysfs_create_file(&slot->kobj,
468                                         &hotplug_slot_attr_cur_bus_speed.attr);
469                 if (retval)
470                         goto exit_cur_speed;
471         }
472
473         if (has_test_file(slot)) {
474                 retval = sysfs_create_file(&slot->kobj,
475                                            &hotplug_slot_attr_test.attr);
476                 if (retval)
477                         goto exit_test;
478         }
479
480         goto exit;
481
482 exit_test:
483         if (has_cur_bus_speed_file(slot))
484                 sysfs_remove_file(&slot->kobj,
485                                   &hotplug_slot_attr_cur_bus_speed.attr);
486 exit_cur_speed:
487         if (has_max_bus_speed_file(slot))
488                 sysfs_remove_file(&slot->kobj,
489                                   &hotplug_slot_attr_max_bus_speed.attr);
490 exit_max_speed:
491         if (has_adapter_file(slot))
492                 sysfs_remove_file(&slot->kobj,
493                                   &hotplug_slot_attr_presence.attr);
494 exit_adapter:
495         if (has_latch_file(slot))
496                 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
497 exit_latch:
498         if (has_attention_file(slot))
499                 sysfs_remove_file(&slot->kobj,
500                                   &hotplug_slot_attr_attention.attr);
501 exit_attention:
502         if (has_power_file(slot))
503                 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
504 exit_power:
505         pci_hp_remove_module_link(slot);
506 exit:
507         return retval;
508 }
509
510 static void fs_remove_slot(struct pci_slot *slot)
511 {
512         if (has_power_file(slot))
513                 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
514
515         if (has_attention_file(slot))
516                 sysfs_remove_file(&slot->kobj,
517                                   &hotplug_slot_attr_attention.attr);
518
519         if (has_latch_file(slot))
520                 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
521
522         if (has_adapter_file(slot))
523                 sysfs_remove_file(&slot->kobj,
524                                   &hotplug_slot_attr_presence.attr);
525
526         if (has_max_bus_speed_file(slot))
527                 sysfs_remove_file(&slot->kobj,
528                                   &hotplug_slot_attr_max_bus_speed.attr);
529
530         if (has_cur_bus_speed_file(slot))
531                 sysfs_remove_file(&slot->kobj,
532                                   &hotplug_slot_attr_cur_bus_speed.attr);
533
534         if (has_test_file(slot))
535                 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
536
537         pci_hp_remove_module_link(slot);
538 }
539
540 static struct hotplug_slot *get_slot_from_name (const char *name)
541 {
542         struct hotplug_slot *slot;
543         struct list_head *tmp;
544
545         list_for_each (tmp, &pci_hotplug_slot_list) {
546                 slot = list_entry (tmp, struct hotplug_slot, slot_list);
547                 if (strcmp(hotplug_slot_name(slot), name) == 0)
548                         return slot;
549         }
550         return NULL;
551 }
552
553 /**
554  * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
555  * @bus: bus this slot is on
556  * @slot: pointer to the &struct hotplug_slot to register
557  * @devnr: device number
558  * @name: name registered with kobject core
559  * @owner: caller module owner
560  * @mod_name: caller module name
561  *
562  * Registers a hotplug slot with the pci hotplug subsystem, which will allow
563  * userspace interaction to the slot.
564  *
565  * Returns 0 if successful, anything else for an error.
566  */
567 int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
568                       int devnr, const char *name,
569                       struct module *owner, const char *mod_name)
570 {
571         int result;
572         struct pci_slot *pci_slot;
573
574         if (slot == NULL)
575                 return -ENODEV;
576         if ((slot->info == NULL) || (slot->ops == NULL))
577                 return -EINVAL;
578         if (slot->release == NULL) {
579                 dbg("Why are you trying to register a hotplug slot "
580                     "without a proper release function?\n");
581                 return -EINVAL;
582         }
583
584         slot->ops->owner = owner;
585         slot->ops->mod_name = mod_name;
586
587         mutex_lock(&pci_hp_mutex);
588         /*
589          * No problems if we call this interface from both ACPI_PCI_SLOT
590          * driver and call it here again. If we've already created the
591          * pci_slot, the interface will simply bump the refcount.
592          */
593         pci_slot = pci_create_slot(bus, devnr, name, slot);
594         if (IS_ERR(pci_slot)) {
595                 result = PTR_ERR(pci_slot);
596                 goto out;
597         }
598
599         slot->pci_slot = pci_slot;
600         pci_slot->hotplug = slot;
601
602         list_add(&slot->slot_list, &pci_hotplug_slot_list);
603
604         result = fs_add_slot(pci_slot);
605         kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
606         dbg("Added slot %s to the list\n", name);
607 out:
608         mutex_unlock(&pci_hp_mutex);
609         return result;
610 }
611
612 /**
613  * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
614  * @hotplug: pointer to the &struct hotplug_slot to deregister
615  *
616  * The @slot must have been registered with the pci hotplug subsystem
617  * previously with a call to pci_hp_register().
618  *
619  * Returns 0 if successful, anything else for an error.
620  */
621 int pci_hp_deregister(struct hotplug_slot *hotplug)
622 {
623         struct hotplug_slot *temp;
624         struct pci_slot *slot;
625
626         if (!hotplug)
627                 return -ENODEV;
628
629         mutex_lock(&pci_hp_mutex);
630         temp = get_slot_from_name(hotplug_slot_name(hotplug));
631         if (temp != hotplug) {
632                 mutex_unlock(&pci_hp_mutex);
633                 return -ENODEV;
634         }
635
636         list_del(&hotplug->slot_list);
637
638         slot = hotplug->pci_slot;
639         fs_remove_slot(slot);
640         dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug));
641
642         hotplug->release(hotplug);
643         slot->hotplug = NULL;
644         pci_destroy_slot(slot);
645         mutex_unlock(&pci_hp_mutex);
646
647         return 0;
648 }
649
650 /**
651  * pci_hp_change_slot_info - changes the slot's information structure in the core
652  * @hotplug: pointer to the slot whose info has changed
653  * @info: pointer to the info copy into the slot's info structure
654  *
655  * @slot must have been registered with the pci 
656  * hotplug subsystem previously with a call to pci_hp_register().
657  *
658  * Returns 0 if successful, anything else for an error.
659  */
660 int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
661                                          struct hotplug_slot_info *info)
662 {
663         struct pci_slot *slot;
664         if (!hotplug || !info)
665                 return -ENODEV;
666         slot = hotplug->pci_slot;
667
668         memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
669
670         return 0;
671 }
672
673 static int __init pci_hotplug_init (void)
674 {
675         int result;
676
677         result = cpci_hotplug_init(debug);
678         if (result) {
679                 err ("cpci_hotplug_init with error %d\n", result);
680                 goto err_cpci;
681         }
682
683         info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
684
685 err_cpci:
686         return result;
687 }
688
689 static void __exit pci_hotplug_exit (void)
690 {
691         cpci_hotplug_exit();
692 }
693
694 module_init(pci_hotplug_init);
695 module_exit(pci_hotplug_exit);
696
697 MODULE_AUTHOR(DRIVER_AUTHOR);
698 MODULE_DESCRIPTION(DRIVER_DESC);
699 MODULE_LICENSE("GPL");
700 module_param(debug, bool, 0644);
701 MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
702
703 EXPORT_SYMBOL_GPL(__pci_hp_register);
704 EXPORT_SYMBOL_GPL(pci_hp_deregister);
705 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);