ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / pci / hotplug / rpaphp_core.c
1 /*
2  * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
3  * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
4  *
5  * All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15  * NON INFRINGEMENT.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * Send feedback to <lxie@us.ibm.com>
23  *
24  */
25 #include <linux/config.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/moduleparam.h>
29 #include <linux/pci.h>
30 #include <linux/slab.h>
31 #include <linux/smp.h>
32 #include <linux/smp_lock.h>
33 #include <linux/init.h>
34 #include <asm/eeh.h>       /* for eeh_add_device() */
35 #include <asm/rtas.h>           /* rtas_call */
36 #include <asm/pci-bridge.h>     /* for pci_controller */
37 #include "../pci.h"             /* for pci_add_new_bus */
38                                 /* and pci_do_scan_bus */
39 #include "rpaphp.h"
40 #include "pci_hotplug.h"
41
42 int debug;
43 static struct semaphore rpaphp_sem;
44 LIST_HEAD(rpaphp_slot_head);
45 int num_slots;
46
47 #define DRIVER_VERSION  "0.1"
48 #define DRIVER_AUTHOR   "Linda Xie <lxie@us.ibm.com>"
49 #define DRIVER_DESC     "RPA HOT Plug PCI Controller Driver"
50
51 #define MAX_LOC_CODE 128
52
53 MODULE_AUTHOR(DRIVER_AUTHOR);
54 MODULE_DESCRIPTION(DRIVER_DESC);
55 MODULE_LICENSE("GPL");
56
57 module_param(debug, int, 0644);
58
59 static int enable_slot(struct hotplug_slot *slot);
60 static int disable_slot(struct hotplug_slot *slot);
61 static int set_attention_status(struct hotplug_slot *slot, u8 value);
62 static int get_power_status(struct hotplug_slot *slot, u8 * value);
63 static int get_attention_status(struct hotplug_slot *slot, u8 * value);
64 static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
65 static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
66 static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
67
68 struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
69         .owner = THIS_MODULE,
70         .enable_slot = enable_slot,
71         .disable_slot = disable_slot,
72         .set_attention_status = set_attention_status,
73         .get_power_status = get_power_status,
74         .get_attention_status = get_attention_status,
75         .get_adapter_status = get_adapter_status,
76         .get_max_bus_speed = get_max_bus_speed,
77         .get_cur_bus_speed = get_cur_bus_speed,
78 };
79
80 static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
81 {
82         if (!hotplug_slot) {
83                 dbg("%s - hotplug_slot == NULL\n", function);
84                 return NULL;
85         }
86         return (struct slot *)hotplug_slot->private;
87 }
88
89 static int rpaphp_get_attention_status(struct slot *slot)
90 {
91         return slot->hotplug_slot->info->attention_status;
92 }
93
94 /**
95  * set_attention_status - set attention LED
96  * echo 0 > attention -- set LED OFF
97  * echo 1 > attention -- set LED ON
98  * echo 2 > attention -- set LED ID(identify, light is blinking)
99  *
100  */
101 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
102 {
103         int retval = 0;
104         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
105
106         if (slot == NULL)
107                 return -ENODEV;
108
109         down(&rpaphp_sem);
110         switch (value) {
111         case 0:
112                 retval = rpaphp_set_attention_status(slot, LED_OFF);
113                 hotplug_slot->info->attention_status = 0;
114                 break;
115         case 1:
116         default:
117                 retval = rpaphp_set_attention_status(slot, LED_ON);
118                 hotplug_slot->info->attention_status = 1;
119                 break;
120         case 2:
121                 retval = rpaphp_set_attention_status(slot, LED_ID);
122                 hotplug_slot->info->attention_status = 2;
123                 break;
124         }
125         up(&rpaphp_sem);
126         return retval;
127 }
128
129 /**
130  * get_power_status - get power status of a slot
131  * @hotplug_slot: slot to get status
132  * @value: pointer to store status
133  *
134  *
135  */
136 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
137 {
138         int retval;
139         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
140
141         if (slot == NULL)
142                 return -ENODEV;
143
144         down(&rpaphp_sem);
145         retval = rpaphp_get_power_status(slot, value);
146         up(&rpaphp_sem);
147         return retval;
148 }
149
150 /**
151  * get_attention_status - get attention LED status
152  *
153  *
154  */
155 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
156 {
157         int retval = 0;
158         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
159
160         if (slot == NULL)
161                 return -ENODEV;
162
163         down(&rpaphp_sem);
164         *value = rpaphp_get_attention_status(slot);
165         up(&rpaphp_sem);
166         return retval;
167 }
168
169 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
170 {
171         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
172         int retval = 0;
173
174         if (slot == NULL)
175                 return -ENODEV;
176         down(&rpaphp_sem);
177         /*  have to go through this */
178         switch (slot->dev_type) {
179         case PCI_DEV:
180                 retval = rpaphp_get_pci_adapter_status(slot, 0, value);
181                 break;
182         case VIO_DEV:
183                 retval = rpaphp_get_vio_adapter_status(slot, 0, value);
184                 break;
185         default:
186                 retval = -EINVAL;
187         }
188         up(&rpaphp_sem);
189         return retval;
190 }
191
192 static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
193 {
194         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
195
196         if (slot == NULL)
197                 return -ENODEV;
198
199         down(&rpaphp_sem);
200         switch (slot->type) {
201         case 1:
202         case 2:
203         case 3:
204         case 4:
205         case 5:
206         case 6:
207                 *value = PCI_SPEED_33MHz;       /* speed for case 1-6 */
208                 break;
209         case 7:
210         case 8:
211                 *value = PCI_SPEED_66MHz;
212                 break;
213         case 11:
214         case 14:
215                 *value = PCI_SPEED_66MHz_PCIX;
216                 break;
217         case 12:
218         case 15:
219                 *value = PCI_SPEED_100MHz_PCIX;
220                 break;
221         case 13:
222         case 16:
223                 *value = PCI_SPEED_133MHz_PCIX;
224                 break;
225         default:
226                 *value = PCI_SPEED_UNKNOWN;
227                 break;
228
229         }
230         up(&rpaphp_sem);
231         return 0;
232 }
233
234 /* return dummy value because not sure if PRA provides any method... */
235 static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
236 {
237         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
238
239         if (slot == NULL)
240                 return -ENODEV;
241
242         *value = PCI_SPEED_UNKNOWN;
243         return 0;
244 }
245
246 int rpaphp_remove_slot(struct slot *slot)
247 {
248         int retval = 0;
249         struct hotplug_slot *php_slot = slot->hotplug_slot;
250
251         list_del(&slot->rpaphp_slot_list);
252         
253         /* remove "php_location" file */
254         rpaphp_sysfs_remove_attr_location(php_slot);
255
256         retval = pci_hp_deregister(php_slot);
257         if (retval)
258                 err("Problem unregistering a slot %s\n", slot->name);
259
260         num_slots--;
261
262         dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
263         return retval;
264 }
265
266 static int is_php_dn(struct device_node *dn, int **indexes, int **names, int **types,
267           int **power_domains)
268 {
269         *indexes = (int *) get_property(dn, "ibm,drc-indexes", NULL);
270         if (!*indexes)
271                 return (0);
272         /* &names[1] contains NULL terminated slot names */
273         *names = (int *) get_property(dn, "ibm,drc-names", NULL);
274         if (!*names)
275                 return (0);
276         /* &types[1] contains NULL terminated slot types */
277         *types = (int *) get_property(dn, "ibm,drc-types", NULL);
278         if (!*types)
279                 return (0);
280         /* power_domains[1...n] are the slot power domains */
281         *power_domains = (int *) get_property(dn,
282                                               "ibm,drc-power-domains", NULL);
283         if (!*power_domains)
284                 return (0);
285         if (strcmp(dn->name, "pci") == 0 &&
286             !get_property(dn, "ibm,fw-pci-hot-plug-ctrl", NULL))
287                 return (0);
288         return (1);
289 }
290
291 static inline int is_vdevice_root(struct device_node *dn)
292 {
293         return !strcmp(dn->name, "vdevice");
294 }
295
296 /*************************************
297  * Add  Hot Plug slot(s) to sysfs
298  *
299  ************************************/
300 int rpaphp_add_slot(struct device_node *dn)
301 {
302         struct slot *slot;
303         int retval = 0;
304         int i;
305         int *indexes, *names, *types, *power_domains;
306         char *name, *type;
307
308         dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name);
309
310         if (dn->parent && is_vdevice_root(dn->parent)) {
311                 /* register a VIO device */
312                 retval = register_vio_slot(dn);
313                 goto exit;
314         }
315
316         /* register PCI devices */
317         if (dn->name != 0 && strcmp(dn->name, "pci") == 0 &&
318             is_php_dn(dn, &indexes, &names, &types, &power_domains)) {
319
320                 name = (char *) &names[1];
321                 type = (char *) &types[1];
322                 for (i = 0; i < indexes[0];
323                      i++,
324                      name += (strlen(name) + 1), type += (strlen(type) + 1)) {
325                         if (!(slot = alloc_slot_struct(dn, indexes[i + 1], name,
326                                                        power_domains[i + 1]))) {
327                                 retval = -ENOMEM;
328                                 goto exit;
329                         }
330                         slot->type = simple_strtoul(type, NULL, 10);
331                         if (slot->type < 1 || slot->type > 16)
332                                 slot->type = 0;
333                         retval = register_pci_slot(slot);
334
335                 }               /* for indexes */
336         }                       /* end of PCI device_node */
337       exit:
338         dbg("%s - Exit: num_slots=%d rc[%d]\n",
339             __FUNCTION__, num_slots, retval);
340         return retval;
341 }
342
343 /*
344  * init_slots - initialize 'struct slot' structures for each slot
345  *
346  */
347 static void init_slots(void)
348 {
349         struct device_node *dn;
350
351         for (dn = find_all_nodes(); dn; dn = dn->next)
352                 rpaphp_add_slot(dn);
353 }
354
355 static int init_rpa(void)
356 {
357
358         init_MUTEX(&rpaphp_sem);
359
360         /* initialize internal data structure etc. */
361         init_slots();
362         if (!num_slots)
363                 return -ENODEV;
364
365         return 0;
366 }
367
368 static void cleanup_slots(void)
369 {
370         struct list_head *tmp, *n;
371         struct slot *slot;
372
373         /*
374          * Unregister all of our slots with the pci_hotplug subsystem,
375          * and free up all memory that we had allocated.
376          * memory will be freed in release_slot callback. 
377          */
378
379         list_for_each_safe(tmp, n, &rpaphp_slot_head) {
380                 slot = list_entry(tmp, struct slot, rpaphp_slot_list);
381                 list_del(&slot->rpaphp_slot_list);
382                 pci_hp_deregister(slot->hotplug_slot);
383         }
384         return;
385 }
386
387 static int __init rpaphp_init(void)
388 {
389         info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
390
391         /* read all the PRA info from the system */
392         return init_rpa();
393 }
394
395 static void __exit rpaphp_exit(void)
396 {
397         cleanup_slots();
398 }
399
400 static int enable_slot(struct hotplug_slot *hotplug_slot)
401 {
402         int retval = 0;
403         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
404
405         if (slot == NULL)
406                 return -ENODEV;
407
408         if (slot->state == CONFIGURED) {
409                 dbg("%s: %s is already enabled\n", __FUNCTION__, slot->name);
410                 goto exit;
411         }
412
413         dbg("ENABLING SLOT %s\n", slot->name);
414         down(&rpaphp_sem);
415         switch (slot->dev_type) {
416         case PCI_DEV:
417                 retval = rpaphp_enable_pci_slot(slot);
418                 break;
419         case VIO_DEV:
420                 retval = rpaphp_enable_vio_slot(slot);
421                 break;
422         default:
423                 retval = -EINVAL;
424         }
425         up(&rpaphp_sem);
426       exit:
427         dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
428         return retval;
429 }
430
431 static int disable_slot(struct hotplug_slot *hotplug_slot)
432 {
433         int retval;
434         struct slot *slot = get_slot(hotplug_slot, __FUNCTION__);
435
436         if (slot == NULL)
437                 return -ENODEV;
438
439         dbg("%s - Entry: slot[%s]\n", __FUNCTION__, slot->name);
440
441         if (slot->state == NOT_CONFIGURED) {
442                 dbg("%s: %s is already disabled\n", __FUNCTION__, slot->name);
443                 goto exit;
444         }
445
446         dbg("DISABLING SLOT %s\n", slot->name);
447         down(&rpaphp_sem);
448         switch (slot->dev_type) {
449         case PCI_DEV:
450                 rpaphp_set_attention_status(slot, LED_ID);
451                 retval = rpaphp_unconfig_pci_adapter(slot);
452                 rpaphp_set_attention_status(slot, LED_OFF);
453                 break;
454         case VIO_DEV:
455                 retval = rpaphp_unconfig_vio_adapter(slot);
456                 break;
457         default:
458                 retval = -ENODEV;
459         }
460         up(&rpaphp_sem);
461       exit:
462         dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
463         return retval;
464 }
465
466 module_init(rpaphp_init);
467 module_exit(rpaphp_exit);
468
469 EXPORT_SYMBOL_GPL(rpaphp_add_slot);
470 EXPORT_SYMBOL_GPL(rpaphp_remove_slot);
471 EXPORT_SYMBOL_GPL(rpaphp_slot_head);