]> Pileus Git - ~andy/linux/blob - drivers/pci/hotplug/rpaphp_core.c
ca95e1515d6f2844185a32774b1d4da16d371639
[~andy/linux] / 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/kernel.h>
26 #include <linux/module.h>
27 #include <linux/moduleparam.h>
28 #include <linux/pci.h>
29 #include <linux/pci_hotplug.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
41 int debug;
42 static struct semaphore rpaphp_sem;
43 LIST_HEAD(rpaphp_slot_head);
44
45 #define DRIVER_VERSION  "0.1"
46 #define DRIVER_AUTHOR   "Linda Xie <lxie@us.ibm.com>"
47 #define DRIVER_DESC     "RPA HOT Plug PCI Controller Driver"
48
49 #define MAX_LOC_CODE 128
50
51 MODULE_AUTHOR(DRIVER_AUTHOR);
52 MODULE_DESCRIPTION(DRIVER_DESC);
53 MODULE_LICENSE("GPL");
54
55 module_param(debug, bool, 0644);
56
57 static int rpaphp_get_attention_status(struct slot *slot)
58 {
59         return slot->hotplug_slot->info->attention_status;
60 }
61
62 /**
63  * set_attention_status - set attention LED
64  * echo 0 > attention -- set LED OFF
65  * echo 1 > attention -- set LED ON
66  * echo 2 > attention -- set LED ID(identify, light is blinking)
67  *
68  */
69 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
70 {
71         int retval = 0;
72         struct slot *slot = (struct slot *)hotplug_slot->private;
73
74         down(&rpaphp_sem);
75         switch (value) {
76         case 0:
77                 retval = rpaphp_set_attention_status(slot, LED_OFF);
78                 hotplug_slot->info->attention_status = 0;
79                 break;
80         case 1:
81         default:
82                 retval = rpaphp_set_attention_status(slot, LED_ON);
83                 hotplug_slot->info->attention_status = 1;
84                 break;
85         case 2:
86                 retval = rpaphp_set_attention_status(slot, LED_ID);
87                 hotplug_slot->info->attention_status = 2;
88                 break;
89         }
90         up(&rpaphp_sem);
91         return retval;
92 }
93
94 /**
95  * get_power_status - get power status of a slot
96  * @hotplug_slot: slot to get status
97  * @value: pointer to store status
98  *
99  *
100  */
101 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
102 {
103         int retval;
104         struct slot *slot = (struct slot *)hotplug_slot->private;
105
106         down(&rpaphp_sem);
107         retval = rpaphp_get_power_status(slot, value);
108         up(&rpaphp_sem);
109         return retval;
110 }
111
112 /**
113  * get_attention_status - get attention LED status
114  *
115  *
116  */
117 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
118 {
119         int retval = 0;
120         struct slot *slot = (struct slot *)hotplug_slot->private;
121
122         down(&rpaphp_sem);
123         *value = rpaphp_get_attention_status(slot);
124         up(&rpaphp_sem);
125         return retval;
126 }
127
128 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
129 {
130         struct slot *slot = (struct slot *)hotplug_slot->private;
131         int retval = 0;
132
133         down(&rpaphp_sem);
134         retval = rpaphp_get_pci_adapter_status(slot, 0, value);
135         up(&rpaphp_sem);
136         return retval;
137 }
138
139 static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
140 {
141         struct slot *slot = (struct slot *)hotplug_slot->private;
142
143         down(&rpaphp_sem);
144         switch (slot->type) {
145         case 1:
146         case 2:
147         case 3:
148         case 4:
149         case 5:
150         case 6:
151                 *value = PCI_SPEED_33MHz;       /* speed for case 1-6 */
152                 break;
153         case 7:
154         case 8:
155                 *value = PCI_SPEED_66MHz;
156                 break;
157         case 11:
158         case 14:
159                 *value = PCI_SPEED_66MHz_PCIX;
160                 break;
161         case 12:
162         case 15:
163                 *value = PCI_SPEED_100MHz_PCIX;
164                 break;
165         case 13:
166         case 16:
167                 *value = PCI_SPEED_133MHz_PCIX;
168                 break;
169         default:
170                 *value = PCI_SPEED_UNKNOWN;
171                 break;
172
173         }
174         up(&rpaphp_sem);
175         return 0;
176 }
177
178 static int get_children_props(struct device_node *dn, const int **drc_indexes,
179                 const int **drc_names, const int **drc_types,
180                 const int **drc_power_domains)
181 {
182         const int *indexes, *names, *types, *domains;
183
184         indexes = get_property(dn, "ibm,drc-indexes", NULL);
185         names = get_property(dn, "ibm,drc-names", NULL);
186         types = get_property(dn, "ibm,drc-types", NULL);
187         domains = get_property(dn, "ibm,drc-power-domains", NULL);
188
189         if (!indexes || !names || !types || !domains) {
190                 /* Slot does not have dynamically-removable children */
191                 return -EINVAL;
192         }
193         if (drc_indexes)
194                 *drc_indexes = indexes;
195         if (drc_names)
196                 /* &drc_names[1] contains NULL terminated slot names */
197                 *drc_names = names;
198         if (drc_types)
199                 /* &drc_types[1] contains NULL terminated slot types */
200                 *drc_types = types;
201         if (drc_power_domains)
202                 *drc_power_domains = domains;
203
204         return 0;
205 }
206
207 /* To get the DRC props describing the current node, first obtain it's
208  * my-drc-index property.  Next obtain the DRC list from it's parent.  Use
209  * the my-drc-index for correlation, and obtain the requested properties.
210  */
211 int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
212                 char **drc_name, char **drc_type, int *drc_power_domain)
213 {
214         const int *indexes, *names;
215         const int *types, *domains;
216         const unsigned int *my_index;
217         char *name_tmp, *type_tmp;
218         int i, rc;
219
220         my_index = get_property(dn, "ibm,my-drc-index", NULL);
221         if (!my_index) {
222                 /* Node isn't DLPAR/hotplug capable */
223                 return -EINVAL;
224         }
225
226         rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
227         if (rc < 0) {
228                 return -EINVAL;
229         }
230
231         name_tmp = (char *) &names[1];
232         type_tmp = (char *) &types[1];
233
234         /* Iterate through parent properties, looking for my-drc-index */
235         for (i = 0; i < indexes[0]; i++) {
236                 if ((unsigned int) indexes[i + 1] == *my_index) {
237                         if (drc_name)
238                                 *drc_name = name_tmp;
239                         if (drc_type)
240                                 *drc_type = type_tmp;
241                         if (drc_index)
242                                 *drc_index = *my_index;
243                         if (drc_power_domain)
244                                 *drc_power_domain = domains[i+1];
245                         return 0;
246                 }
247                 name_tmp += (strlen(name_tmp) + 1);
248                 type_tmp += (strlen(type_tmp) + 1);
249         }
250
251         return -EINVAL;
252 }
253
254 static int is_php_type(char *drc_type)
255 {
256         unsigned long value;
257         char *endptr;
258
259         /* PCI Hotplug nodes have an integer for drc_type */
260         value = simple_strtoul(drc_type, &endptr, 10);
261         if (endptr == drc_type)
262                 return 0;
263
264         return 1;
265 }
266
267 static int is_php_dn(struct device_node *dn, const int **indexes,
268                 const int **names, const int **types, const int **power_domains)
269 {
270         const int *drc_types;
271         int rc;
272
273         rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
274         if (rc >= 0) {
275                 if (is_php_type((char *) &drc_types[1])) {
276                         *types = drc_types;
277                         return 1;
278                 }
279         }
280
281         return 0;
282 }
283
284 /**
285  * rpaphp_add_slot -- add hotplug or dlpar slot
286  *
287  *      rpaphp not only registers PCI hotplug slots(HOTPLUG), 
288  *      but also logical DR slots(EMBEDDED).
289  *      HOTPLUG slot: An adapter can be physically added/removed. 
290  *      EMBEDDED slot: An adapter can be logically removed/added
291  *                from/to a partition with the slot.
292  */
293 int rpaphp_add_slot(struct device_node *dn)
294 {
295         struct slot *slot;
296         int retval = 0;
297         int i;
298         const int *indexes, *names, *types, *power_domains;
299         char *name, *type;
300
301         if (!dn->name || strcmp(dn->name, "pci"))
302                 return 0;
303
304         if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
305                 return 0;
306
307         dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name);
308
309         /* register PCI devices */
310         name = (char *) &names[1];
311         type = (char *) &types[1];
312         for (i = 0; i < indexes[0]; i++) {
313
314                 slot = alloc_slot_struct(dn, indexes[i + 1], name, power_domains[i + 1]);
315                 if (!slot)
316                         return -ENOMEM;
317
318                 slot->type = simple_strtoul(type, NULL, 10);
319                                 
320                 dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
321                                 indexes[i + 1], name, type);
322
323                 retval = rpaphp_register_pci_slot(slot);
324                 if (retval)
325                         dealloc_slot_struct(slot);
326
327                 name += strlen(name) + 1;
328                 type += strlen(type) + 1;
329         }
330         dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
331
332         /* XXX FIXME: reports a failure only if last entry in loop failed */
333         return retval;
334 }
335
336 static void __exit cleanup_slots(void)
337 {
338         struct list_head *tmp, *n;
339         struct slot *slot;
340
341         /*
342          * Unregister all of our slots with the pci_hotplug subsystem,
343          * and free up all memory that we had allocated.
344          * memory will be freed in release_slot callback. 
345          */
346
347         list_for_each_safe(tmp, n, &rpaphp_slot_head) {
348                 slot = list_entry(tmp, struct slot, rpaphp_slot_list);
349                 list_del(&slot->rpaphp_slot_list);
350                 pci_hp_deregister(slot->hotplug_slot);
351         }
352         return;
353 }
354
355 static int __init rpaphp_init(void)
356 {
357         struct device_node *dn = NULL;
358
359         info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
360         init_MUTEX(&rpaphp_sem);
361
362         while ((dn = of_find_node_by_name(dn, "pci")))
363                 rpaphp_add_slot(dn);
364
365         return 0;
366 }
367
368 static void __exit rpaphp_exit(void)
369 {
370         cleanup_slots();
371 }
372
373 static int __enable_slot(struct slot *slot)
374 {
375         int state;
376         int retval;
377
378         if (slot->state == CONFIGURED)
379                 return 0;
380
381         retval = rpaphp_get_sensor_state(slot, &state);
382         if (retval)
383                 return retval;
384
385         if (state == PRESENT) {
386                 pcibios_add_pci_devices(slot->bus);
387                 slot->state = CONFIGURED;
388         } else if (state == EMPTY) {
389                 slot->state = EMPTY;
390         } else {
391                 err("%s: slot[%s] is in invalid state\n", __FUNCTION__, slot->name);
392                 slot->state = NOT_VALID;
393                 return -EINVAL;
394         }
395         return 0;
396 }
397
398 static int enable_slot(struct hotplug_slot *hotplug_slot)
399 {
400         int retval;
401         struct slot *slot = (struct slot *)hotplug_slot->private;
402
403         down(&rpaphp_sem);
404         retval = __enable_slot(slot);
405         up(&rpaphp_sem);
406
407         return retval;
408 }
409
410 static int __disable_slot(struct slot *slot)
411 {
412         struct pci_dev *dev, *tmp;
413
414         if (slot->state == NOT_CONFIGURED)
415                 return -EINVAL;
416
417         list_for_each_entry_safe(dev, tmp, &slot->bus->devices, bus_list) {
418                 eeh_remove_bus_device(dev);
419                 pci_remove_bus_device(dev);
420         }
421
422         slot->state = NOT_CONFIGURED;
423         return 0;
424 }
425
426 static int disable_slot(struct hotplug_slot *hotplug_slot)
427 {
428         struct slot *slot = (struct slot *)hotplug_slot->private;
429         int retval;
430
431         down(&rpaphp_sem);
432         retval = __disable_slot (slot);
433         up(&rpaphp_sem);
434
435         return retval;
436 }
437
438 struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
439         .owner = THIS_MODULE,
440         .enable_slot = enable_slot,
441         .disable_slot = disable_slot,
442         .set_attention_status = set_attention_status,
443         .get_power_status = get_power_status,
444         .get_attention_status = get_attention_status,
445         .get_adapter_status = get_adapter_status,
446         .get_max_bus_speed = get_max_bus_speed,
447 };
448
449 module_init(rpaphp_init);
450 module_exit(rpaphp_exit);
451
452 EXPORT_SYMBOL_GPL(rpaphp_add_slot);
453 EXPORT_SYMBOL_GPL(rpaphp_slot_head);
454 EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);