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