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