diff options
Diffstat (limited to 'drivers/pci/hotplug/rpaphp_core.c')
-rw-r--r-- | drivers/pci/hotplug/rpaphp_core.c | 200 |
1 files changed, 91 insertions, 109 deletions
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 71a2cb8baa4a..847936fe327e 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c | |||
@@ -39,9 +39,7 @@ | |||
39 | #include "rpaphp.h" | 39 | #include "rpaphp.h" |
40 | 40 | ||
41 | int debug; | 41 | int debug; |
42 | static struct semaphore rpaphp_sem; | ||
43 | LIST_HEAD(rpaphp_slot_head); | 42 | LIST_HEAD(rpaphp_slot_head); |
44 | int num_slots; | ||
45 | 43 | ||
46 | #define DRIVER_VERSION "0.1" | 44 | #define DRIVER_VERSION "0.1" |
47 | #define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>" | 45 | #define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>" |
@@ -55,11 +53,6 @@ MODULE_LICENSE("GPL"); | |||
55 | 53 | ||
56 | module_param(debug, bool, 0644); | 54 | module_param(debug, bool, 0644); |
57 | 55 | ||
58 | static int rpaphp_get_attention_status(struct slot *slot) | ||
59 | { | ||
60 | return slot->hotplug_slot->info->attention_status; | ||
61 | } | ||
62 | |||
63 | /** | 56 | /** |
64 | * set_attention_status - set attention LED | 57 | * set_attention_status - set attention LED |
65 | * echo 0 > attention -- set LED OFF | 58 | * echo 0 > attention -- set LED OFF |
@@ -69,79 +62,75 @@ static int rpaphp_get_attention_status(struct slot *slot) | |||
69 | */ | 62 | */ |
70 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) | 63 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) |
71 | { | 64 | { |
72 | int retval = 0; | 65 | int rc; |
73 | struct slot *slot = (struct slot *)hotplug_slot->private; | 66 | struct slot *slot = (struct slot *)hotplug_slot->private; |
74 | 67 | ||
75 | down(&rpaphp_sem); | ||
76 | switch (value) { | 68 | switch (value) { |
77 | case 0: | 69 | case 0: |
78 | retval = rpaphp_set_attention_status(slot, LED_OFF); | ||
79 | hotplug_slot->info->attention_status = 0; | ||
80 | break; | ||
81 | case 1: | 70 | case 1: |
82 | default: | ||
83 | retval = rpaphp_set_attention_status(slot, LED_ON); | ||
84 | hotplug_slot->info->attention_status = 1; | ||
85 | break; | ||
86 | case 2: | 71 | case 2: |
87 | retval = rpaphp_set_attention_status(slot, LED_ID); | 72 | break; |
88 | hotplug_slot->info->attention_status = 2; | 73 | default: |
74 | value = 1; | ||
89 | break; | 75 | break; |
90 | } | 76 | } |
91 | up(&rpaphp_sem); | 77 | |
92 | return retval; | 78 | rc = rtas_set_indicator(DR_INDICATOR, slot->index, value); |
79 | if (!rc) | ||
80 | hotplug_slot->info->attention_status = value; | ||
81 | |||
82 | return rc; | ||
93 | } | 83 | } |
94 | 84 | ||
95 | /** | 85 | /** |
96 | * get_power_status - get power status of a slot | 86 | * get_power_status - get power status of a slot |
97 | * @hotplug_slot: slot to get status | 87 | * @hotplug_slot: slot to get status |
98 | * @value: pointer to store status | 88 | * @value: pointer to store status |
99 | * | ||
100 | * | ||
101 | */ | 89 | */ |
102 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) | 90 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) |
103 | { | 91 | { |
104 | int retval; | 92 | int retval, level; |
105 | struct slot *slot = (struct slot *)hotplug_slot->private; | 93 | struct slot *slot = (struct slot *)hotplug_slot->private; |
106 | 94 | ||
107 | down(&rpaphp_sem); | 95 | retval = rtas_get_power_level (slot->power_domain, &level); |
108 | retval = rpaphp_get_power_status(slot, value); | 96 | if (!retval) |
109 | up(&rpaphp_sem); | 97 | *value = level; |
110 | return retval; | 98 | return retval; |
111 | } | 99 | } |
112 | 100 | ||
113 | /** | 101 | /** |
114 | * get_attention_status - get attention LED status | 102 | * get_attention_status - get attention LED status |
115 | * | ||
116 | * | ||
117 | */ | 103 | */ |
118 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) | 104 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) |
119 | { | 105 | { |
120 | int retval = 0; | ||
121 | struct slot *slot = (struct slot *)hotplug_slot->private; | 106 | struct slot *slot = (struct slot *)hotplug_slot->private; |
122 | 107 | *value = slot->hotplug_slot->info->attention_status; | |
123 | down(&rpaphp_sem); | 108 | return 0; |
124 | *value = rpaphp_get_attention_status(slot); | ||
125 | up(&rpaphp_sem); | ||
126 | return retval; | ||
127 | } | 109 | } |
128 | 110 | ||
129 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) | 111 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) |
130 | { | 112 | { |
131 | struct slot *slot = (struct slot *)hotplug_slot->private; | 113 | struct slot *slot = (struct slot *)hotplug_slot->private; |
132 | int retval = 0; | 114 | int rc, state; |
133 | 115 | ||
134 | down(&rpaphp_sem); | 116 | rc = rpaphp_get_sensor_state(slot, &state); |
135 | retval = rpaphp_get_pci_adapter_status(slot, 0, value); | 117 | |
136 | up(&rpaphp_sem); | 118 | *value = NOT_VALID; |
137 | return retval; | 119 | if (rc) |
120 | return rc; | ||
121 | |||
122 | if (state == EMPTY) | ||
123 | *value = EMPTY; | ||
124 | else if (state == PRESENT) | ||
125 | *value = slot->state; | ||
126 | |||
127 | return 0; | ||
138 | } | 128 | } |
139 | 129 | ||
140 | static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | 130 | static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) |
141 | { | 131 | { |
142 | struct slot *slot = (struct slot *)hotplug_slot->private; | 132 | struct slot *slot = (struct slot *)hotplug_slot->private; |
143 | 133 | ||
144 | down(&rpaphp_sem); | ||
145 | switch (slot->type) { | 134 | switch (slot->type) { |
146 | case 1: | 135 | case 1: |
147 | case 2: | 136 | case 2: |
@@ -172,7 +161,6 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe | |||
172 | break; | 161 | break; |
173 | 162 | ||
174 | } | 163 | } |
175 | up(&rpaphp_sem); | ||
176 | return 0; | 164 | return 0; |
177 | } | 165 | } |
178 | 166 | ||
@@ -265,6 +253,14 @@ static int is_php_type(char *drc_type) | |||
265 | return 1; | 253 | return 1; |
266 | } | 254 | } |
267 | 255 | ||
256 | /** | ||
257 | * is_php_dn() - return 1 if this is a hotpluggable pci slot, else 0 | ||
258 | * | ||
259 | * This routine will return true only if the device node is | ||
260 | * a hotpluggable slot. This routine will return false | ||
261 | * for built-in pci slots (even when the built-in slots are | ||
262 | * dlparable.) | ||
263 | */ | ||
268 | static int is_php_dn(struct device_node *dn, const int **indexes, | 264 | static int is_php_dn(struct device_node *dn, const int **indexes, |
269 | const int **names, const int **types, const int **power_domains) | 265 | const int **names, const int **types, const int **power_domains) |
270 | { | 266 | { |
@@ -272,24 +268,31 @@ static int is_php_dn(struct device_node *dn, const int **indexes, | |||
272 | int rc; | 268 | int rc; |
273 | 269 | ||
274 | rc = get_children_props(dn, indexes, names, &drc_types, power_domains); | 270 | rc = get_children_props(dn, indexes, names, &drc_types, power_domains); |
275 | if (rc >= 0) { | 271 | if (rc < 0) |
276 | if (is_php_type((char *) &drc_types[1])) { | 272 | return 0; |
277 | *types = drc_types; | ||
278 | return 1; | ||
279 | } | ||
280 | } | ||
281 | 273 | ||
282 | return 0; | 274 | if (!is_php_type((char *) &drc_types[1])) |
275 | return 0; | ||
276 | |||
277 | *types = drc_types; | ||
278 | return 1; | ||
283 | } | 279 | } |
284 | 280 | ||
285 | /** | 281 | /** |
286 | * rpaphp_add_slot -- add hotplug or dlpar slot | 282 | * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem. |
283 | * @dn device node of slot | ||
284 | * | ||
285 | * This subroutine will register a hotplugable slot with the | ||
286 | * PCI hotplug infrastructure. This routine is typicaly called | ||
287 | * during boot time, if the hotplug slots are present at boot time, | ||
288 | * or is called later, by the dlpar add code, if the slot is | ||
289 | * being dynamically added during runtime. | ||
290 | * | ||
291 | * If the device node points at an embedded (built-in) slot, this | ||
292 | * routine will just return without doing anything, since embedded | ||
293 | * slots cannot be hotplugged. | ||
287 | * | 294 | * |
288 | * rpaphp not only registers PCI hotplug slots(HOTPLUG), | 295 | * To remove a slot, it suffices to call rpaphp_deregister_slot() |
289 | * but also logical DR slots(EMBEDDED). | ||
290 | * HOTPLUG slot: An adapter can be physically added/removed. | ||
291 | * EMBEDDED slot: An adapter can be logically removed/added | ||
292 | * from/to a partition with the slot. | ||
293 | */ | 296 | */ |
294 | int rpaphp_add_slot(struct device_node *dn) | 297 | int rpaphp_add_slot(struct device_node *dn) |
295 | { | 298 | { |
@@ -299,34 +302,42 @@ int rpaphp_add_slot(struct device_node *dn) | |||
299 | const int *indexes, *names, *types, *power_domains; | 302 | const int *indexes, *names, *types, *power_domains; |
300 | char *name, *type; | 303 | char *name, *type; |
301 | 304 | ||
305 | if (!dn->name || strcmp(dn->name, "pci")) | ||
306 | return 0; | ||
307 | |||
308 | /* If this is not a hotplug slot, return without doing anything. */ | ||
309 | if (!is_php_dn(dn, &indexes, &names, &types, &power_domains)) | ||
310 | return 0; | ||
311 | |||
302 | dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name); | 312 | dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name); |
303 | 313 | ||
304 | /* register PCI devices */ | 314 | /* register PCI devices */ |
305 | if (dn->name != 0 && strcmp(dn->name, "pci") == 0) { | 315 | name = (char *) &names[1]; |
306 | if (!is_php_dn(dn, &indexes, &names, &types, &power_domains)) | 316 | type = (char *) &types[1]; |
307 | goto exit; | 317 | for (i = 0; i < indexes[0]; i++) { |
308 | 318 | ||
309 | name = (char *) &names[1]; | 319 | slot = alloc_slot_struct(dn, indexes[i + 1], name, power_domains[i + 1]); |
310 | type = (char *) &types[1]; | 320 | if (!slot) |
311 | for (i = 0; i < indexes[0]; i++, | 321 | return -ENOMEM; |
312 | name += (strlen(name) + 1), type += (strlen(type) + 1)) { | 322 | |
313 | 323 | slot->type = simple_strtoul(type, NULL, 10); | |
314 | if (!(slot = alloc_slot_struct(dn, indexes[i + 1], name, | ||
315 | power_domains[i + 1]))) { | ||
316 | retval = -ENOMEM; | ||
317 | goto exit; | ||
318 | } | ||
319 | slot->type = simple_strtoul(type, NULL, 10); | ||
320 | 324 | ||
321 | dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n", | 325 | dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n", |
322 | indexes[i + 1], name, type); | 326 | indexes[i + 1], name, type); |
323 | 327 | ||
324 | retval = rpaphp_register_pci_slot(slot); | 328 | retval = rpaphp_enable_slot(slot); |
325 | } | 329 | if (!retval) |
330 | retval = rpaphp_register_slot(slot); | ||
331 | |||
332 | if (retval) | ||
333 | dealloc_slot_struct(slot); | ||
334 | |||
335 | name += strlen(name) + 1; | ||
336 | type += strlen(type) + 1; | ||
326 | } | 337 | } |
327 | exit: | 338 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); |
328 | dbg("%s - Exit: num_slots=%d rc[%d]\n", | 339 | |
329 | __FUNCTION__, num_slots, retval); | 340 | /* XXX FIXME: reports a failure only if last entry in loop failed */ |
330 | return retval; | 341 | return retval; |
331 | } | 342 | } |
332 | 343 | ||
@@ -354,7 +365,6 @@ static int __init rpaphp_init(void) | |||
354 | struct device_node *dn = NULL; | 365 | struct device_node *dn = NULL; |
355 | 366 | ||
356 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | 367 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); |
357 | init_MUTEX(&rpaphp_sem); | ||
358 | 368 | ||
359 | while ((dn = of_find_node_by_name(dn, "pci"))) | 369 | while ((dn = of_find_node_by_name(dn, "pci"))) |
360 | rpaphp_add_slot(dn); | 370 | rpaphp_add_slot(dn); |
@@ -367,8 +377,9 @@ static void __exit rpaphp_exit(void) | |||
367 | cleanup_slots(); | 377 | cleanup_slots(); |
368 | } | 378 | } |
369 | 379 | ||
370 | static int __enable_slot(struct slot *slot) | 380 | static int enable_slot(struct hotplug_slot *hotplug_slot) |
371 | { | 381 | { |
382 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
372 | int state; | 383 | int state; |
373 | int retval; | 384 | int retval; |
374 | 385 | ||
@@ -392,46 +403,17 @@ static int __enable_slot(struct slot *slot) | |||
392 | return 0; | 403 | return 0; |
393 | } | 404 | } |
394 | 405 | ||
395 | static int enable_slot(struct hotplug_slot *hotplug_slot) | 406 | static int disable_slot(struct hotplug_slot *hotplug_slot) |
396 | { | 407 | { |
397 | int retval; | ||
398 | struct slot *slot = (struct slot *)hotplug_slot->private; | 408 | struct slot *slot = (struct slot *)hotplug_slot->private; |
399 | |||
400 | down(&rpaphp_sem); | ||
401 | retval = __enable_slot(slot); | ||
402 | up(&rpaphp_sem); | ||
403 | |||
404 | return retval; | ||
405 | } | ||
406 | |||
407 | static int __disable_slot(struct slot *slot) | ||
408 | { | ||
409 | struct pci_dev *dev, *tmp; | ||
410 | |||
411 | if (slot->state == NOT_CONFIGURED) | 409 | if (slot->state == NOT_CONFIGURED) |
412 | return -EINVAL; | 410 | return -EINVAL; |
413 | 411 | ||
414 | list_for_each_entry_safe(dev, tmp, &slot->bus->devices, bus_list) { | 412 | pcibios_remove_pci_devices(slot->bus); |
415 | eeh_remove_bus_device(dev); | ||
416 | pci_remove_bus_device(dev); | ||
417 | } | ||
418 | |||
419 | slot->state = NOT_CONFIGURED; | 413 | slot->state = NOT_CONFIGURED; |
420 | return 0; | 414 | return 0; |
421 | } | 415 | } |
422 | 416 | ||
423 | static int disable_slot(struct hotplug_slot *hotplug_slot) | ||
424 | { | ||
425 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
426 | int retval; | ||
427 | |||
428 | down(&rpaphp_sem); | ||
429 | retval = __disable_slot (slot); | ||
430 | up(&rpaphp_sem); | ||
431 | |||
432 | return retval; | ||
433 | } | ||
434 | |||
435 | struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { | 417 | struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { |
436 | .owner = THIS_MODULE, | 418 | .owner = THIS_MODULE, |
437 | .enable_slot = enable_slot, | 419 | .enable_slot = enable_slot, |