diff options
| -rw-r--r-- | drivers/pci/hotplug/rpadlpar_core.c | 44 | ||||
| -rw-r--r-- | drivers/pci/hotplug/rpaphp.h | 7 | ||||
| -rw-r--r-- | drivers/pci/hotplug/rpaphp_pci.c | 141 |
3 files changed, 75 insertions, 117 deletions
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index d7f1319f167a..7f868edaa72d 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c | |||
| @@ -198,28 +198,6 @@ static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn) | |||
| 198 | return dev; | 198 | return dev; |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | static int dlpar_pci_remove_bus(struct pci_dev *bridge_dev) | ||
| 202 | { | ||
| 203 | struct pci_bus *secondary_bus; | ||
| 204 | |||
| 205 | if (!bridge_dev) { | ||
| 206 | printk(KERN_ERR "%s: unexpected null device\n", | ||
| 207 | __FUNCTION__); | ||
| 208 | return -EINVAL; | ||
| 209 | } | ||
| 210 | |||
| 211 | secondary_bus = bridge_dev->subordinate; | ||
| 212 | |||
| 213 | if (unmap_bus_range(secondary_bus)) { | ||
| 214 | printk(KERN_ERR "%s: failed to unmap bus range\n", | ||
| 215 | __FUNCTION__); | ||
| 216 | return -ERANGE; | ||
| 217 | } | ||
| 218 | |||
| 219 | pci_remove_bus_device(bridge_dev); | ||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | |||
| 223 | static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) | 201 | static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) |
| 224 | { | 202 | { |
| 225 | struct pci_dev *dev; | 203 | struct pci_dev *dev; |
| @@ -415,14 +393,7 @@ static int dlpar_remove_vio_slot(struct device_node *dn, char *drc_name) | |||
| 415 | */ | 393 | */ |
| 416 | int dlpar_remove_pci_slot(struct slot *slot, char *drc_name) | 394 | int dlpar_remove_pci_slot(struct slot *slot, char *drc_name) |
| 417 | { | 395 | { |
| 418 | struct pci_dev *bridge_dev; | 396 | struct pci_bus *bus = slot->bus; |
| 419 | |||
| 420 | bridge_dev = slot->bridge; | ||
| 421 | if (!bridge_dev) { | ||
| 422 | printk(KERN_ERR "%s: unexpected null bridge device\n", | ||
| 423 | __FUNCTION__); | ||
| 424 | return -EIO; | ||
| 425 | } | ||
| 426 | 397 | ||
| 427 | /* Remove hotplug slot */ | 398 | /* Remove hotplug slot */ |
| 428 | if (rpaphp_remove_slot(slot)) { | 399 | if (rpaphp_remove_slot(slot)) { |
| @@ -431,13 +402,14 @@ int dlpar_remove_pci_slot(struct slot *slot, char *drc_name) | |||
| 431 | return -EIO; | 402 | return -EIO; |
| 432 | } | 403 | } |
| 433 | 404 | ||
| 434 | /* Remove pci bus */ | 405 | if (unmap_bus_range(bus)) { |
| 435 | 406 | printk(KERN_ERR "%s: failed to unmap bus range\n", | |
| 436 | if (dlpar_pci_remove_bus(bridge_dev)) { | 407 | __FUNCTION__); |
| 437 | printk(KERN_ERR "%s: unable to remove pci bus %s\n", | 408 | return -ERANGE; |
| 438 | __FUNCTION__, drc_name); | ||
| 439 | return -EIO; | ||
| 440 | } | 409 | } |
| 410 | |||
| 411 | BUG_ON(!bus->self); | ||
| 412 | pci_remove_bus_device(bus->self); | ||
| 441 | return 0; | 413 | return 0; |
| 442 | } | 414 | } |
| 443 | 415 | ||
diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h index 2d9f420dfa4f..9f050fde80c2 100644 --- a/drivers/pci/hotplug/rpaphp.h +++ b/drivers/pci/hotplug/rpaphp.h | |||
| @@ -80,10 +80,9 @@ struct slot { | |||
| 80 | char *name; | 80 | char *name; |
| 81 | char *location; | 81 | char *location; |
| 82 | u8 removable; | 82 | u8 removable; |
| 83 | struct device_node *dn; /* slot's device_node in OFDT */ | 83 | struct device_node *dn; |
| 84 | /* dn has phb info */ | 84 | struct pci_bus *bus; |
| 85 | struct pci_dev *bridge; /* slot's pci_dev in pci_devices */ | 85 | struct list_head *pci_devs; |
| 86 | struct list_head *pci_devs; /* pci_devs in PCI slot */ | ||
| 87 | struct hotplug_slot *hotplug_slot; | 86 | struct hotplug_slot *hotplug_slot; |
| 88 | }; | 87 | }; |
| 89 | 88 | ||
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c index 30d10fcc24b2..54fff5fb0094 100644 --- a/drivers/pci/hotplug/rpaphp_pci.c +++ b/drivers/pci/hotplug/rpaphp_pci.c | |||
| @@ -45,6 +45,32 @@ struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn) | |||
| 45 | return dev; | 45 | return dev; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | struct pci_bus *find_bus_among_children(struct pci_bus *bus, | ||
| 49 | struct device_node *dn) | ||
| 50 | { | ||
| 51 | struct pci_bus *child = NULL; | ||
| 52 | struct list_head *tmp; | ||
| 53 | struct device_node *busdn; | ||
| 54 | |||
| 55 | busdn = pci_bus_to_OF_node(bus); | ||
| 56 | if (busdn == dn) | ||
| 57 | return bus; | ||
| 58 | |||
| 59 | list_for_each(tmp, &bus->children) { | ||
| 60 | child = find_bus_among_children(pci_bus_b(tmp), dn); | ||
| 61 | if (child) | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | return child; | ||
| 65 | } | ||
| 66 | |||
| 67 | struct pci_bus *rpaphp_find_pci_bus(struct device_node *dn) | ||
| 68 | { | ||
| 69 | BUG_ON(!dn->phb || !dn->phb->bus); | ||
| 70 | |||
| 71 | return find_bus_among_children(dn->phb->bus, dn); | ||
| 72 | } | ||
| 73 | |||
| 48 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev); | 74 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev); |
| 49 | 75 | ||
| 50 | int rpaphp_claim_resource(struct pci_dev *dev, int resource) | 76 | int rpaphp_claim_resource(struct pci_dev *dev, int resource) |
| @@ -69,11 +95,6 @@ int rpaphp_claim_resource(struct pci_dev *dev, int resource) | |||
| 69 | 95 | ||
| 70 | EXPORT_SYMBOL_GPL(rpaphp_claim_resource); | 96 | EXPORT_SYMBOL_GPL(rpaphp_claim_resource); |
| 71 | 97 | ||
| 72 | static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot) | ||
| 73 | { | ||
| 74 | return rpaphp_find_pci_dev(slot->dn); | ||
| 75 | } | ||
| 76 | |||
| 77 | static int rpaphp_get_sensor_state(struct slot *slot, int *state) | 98 | static int rpaphp_get_sensor_state(struct slot *slot, int *state) |
| 78 | { | 99 | { |
| 79 | int rc; | 100 | int rc; |
| @@ -226,20 +247,22 @@ static int rpaphp_pci_config_bridge(struct pci_dev *dev) | |||
| 226 | static struct pci_dev * | 247 | static struct pci_dev * |
| 227 | rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus) | 248 | rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus) |
| 228 | { | 249 | { |
| 229 | struct device_node *eads_first_child = dn->child; | ||
| 230 | struct pci_dev *dev = NULL; | 250 | struct pci_dev *dev = NULL; |
| 251 | int slotno; | ||
| 231 | int num; | 252 | int num; |
| 232 | 253 | ||
| 233 | dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name); | 254 | dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name); |
| 234 | 255 | ||
| 235 | if (eads_first_child) { | 256 | if (dn->child) { |
| 236 | /* pci_scan_slot should find all children of EADs */ | 257 | slotno = PCI_SLOT(dn->child->devfn); |
| 237 | num = pci_scan_slot(bus, PCI_DEVFN(PCI_SLOT(eads_first_child->devfn), 0)); | 258 | |
| 259 | /* pci_scan_slot should find all children */ | ||
| 260 | num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); | ||
| 238 | if (num) { | 261 | if (num) { |
| 239 | rpaphp_fixup_new_pci_devices(bus, 1); | 262 | rpaphp_fixup_new_pci_devices(bus, 1); |
| 240 | pci_bus_add_devices(bus); | 263 | pci_bus_add_devices(bus); |
| 241 | } | 264 | } |
| 242 | dev = rpaphp_find_pci_dev(eads_first_child); | 265 | dev = rpaphp_find_pci_dev(dn->child); |
| 243 | if (!dev) { | 266 | if (!dev) { |
| 244 | err("No new device found\n"); | 267 | err("No new device found\n"); |
| 245 | return NULL; | 268 | return NULL; |
| @@ -273,31 +296,19 @@ static void print_slot_pci_funcs(struct slot *slot) | |||
| 273 | 296 | ||
| 274 | static int rpaphp_config_pci_adapter(struct slot *slot) | 297 | static int rpaphp_config_pci_adapter(struct slot *slot) |
| 275 | { | 298 | { |
| 276 | struct pci_bus *pci_bus; | ||
| 277 | struct pci_dev *dev; | 299 | struct pci_dev *dev; |
| 278 | int rc = -ENODEV; | 300 | int rc = -ENODEV; |
| 279 | 301 | ||
| 280 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name); | 302 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name); |
| 281 | 303 | ||
| 282 | if (slot->bridge) { | 304 | enable_eeh(slot->dn); |
| 283 | 305 | dev = rpaphp_pci_config_slot(slot->dn, slot->bus); | |
| 284 | pci_bus = slot->bridge->subordinate; | 306 | if (!dev) { |
| 285 | if (!pci_bus) { | 307 | err("%s: can't find any devices.\n", __FUNCTION__); |
| 286 | err("%s: can't find bus structure\n", __FUNCTION__); | 308 | goto exit; |
| 287 | goto exit; | ||
| 288 | } | ||
| 289 | enable_eeh(slot->dn); | ||
| 290 | dev = rpaphp_pci_config_slot(slot->dn, pci_bus); | ||
| 291 | if (!dev) { | ||
| 292 | err("%s: can't find any devices.\n", __FUNCTION__); | ||
| 293 | goto exit; | ||
| 294 | } | ||
| 295 | print_slot_pci_funcs(slot); | ||
| 296 | rc = 0; | ||
| 297 | } else { | ||
| 298 | /* slot is not enabled */ | ||
| 299 | err("slot doesn't have pci_dev structure\n"); | ||
| 300 | } | 309 | } |
| 310 | print_slot_pci_funcs(slot); | ||
| 311 | rc = 0; | ||
| 301 | exit: | 312 | exit: |
| 302 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); | 313 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); |
| 303 | return rc; | 314 | return rc; |
| @@ -323,13 +334,14 @@ static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) | |||
| 323 | 334 | ||
| 324 | int rpaphp_unconfig_pci_adapter(struct slot *slot) | 335 | int rpaphp_unconfig_pci_adapter(struct slot *slot) |
| 325 | { | 336 | { |
| 326 | struct pci_dev *dev; | 337 | struct pci_dev *dev, *tmp; |
| 327 | int retval = 0; | 338 | int retval = 0; |
| 328 | 339 | ||
| 329 | list_for_each_entry(dev, slot->pci_devs, bus_list) | 340 | list_for_each_entry_safe(dev, tmp, slot->pci_devs, bus_list) { |
| 330 | rpaphp_eeh_remove_bus_device(dev); | 341 | rpaphp_eeh_remove_bus_device(dev); |
| 342 | pci_remove_bus_device(dev); | ||
| 343 | } | ||
| 331 | 344 | ||
| 332 | pci_remove_behind_bridge(slot->bridge); | ||
| 333 | slot->state = NOT_CONFIGURED; | 345 | slot->state = NOT_CONFIGURED; |
| 334 | info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__, | 346 | info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__, |
| 335 | slot->name); | 347 | slot->name); |
| @@ -352,66 +364,41 @@ static int setup_pci_hotplug_slot_info(struct slot *slot) | |||
| 352 | return 0; | 364 | return 0; |
| 353 | } | 365 | } |
| 354 | 366 | ||
| 355 | static int set_phb_slot_name(struct slot *slot) | 367 | static void set_slot_name(struct slot *slot) |
| 356 | { | 368 | { |
| 357 | struct device_node *dn; | 369 | struct pci_bus *bus = slot->bus; |
| 358 | struct pci_controller *phb; | 370 | struct pci_dev *bridge; |
| 359 | struct pci_bus *bus; | ||
| 360 | |||
| 361 | dn = slot->dn; | ||
| 362 | if (!dn) { | ||
| 363 | return -EINVAL; | ||
| 364 | } | ||
| 365 | phb = dn->phb; | ||
| 366 | if (!phb) { | ||
| 367 | return -EINVAL; | ||
| 368 | } | ||
| 369 | bus = phb->bus; | ||
| 370 | if (!bus) { | ||
| 371 | return -EINVAL; | ||
| 372 | } | ||
| 373 | 371 | ||
| 374 | sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus), | 372 | bridge = bus->self; |
| 375 | bus->number, 0, 0); | 373 | if (bridge) |
| 376 | return 0; | 374 | strcpy(slot->name, pci_name(bridge)); |
| 375 | else | ||
| 376 | sprintf(slot->name, "%04x:%02x:00.0", pci_domain_nr(bus), | ||
| 377 | bus->number); | ||
| 377 | } | 378 | } |
| 378 | 379 | ||
| 379 | static int setup_pci_slot(struct slot *slot) | 380 | static int setup_pci_slot(struct slot *slot) |
| 380 | { | 381 | { |
| 382 | struct device_node *dn = slot->dn; | ||
| 381 | struct pci_bus *bus; | 383 | struct pci_bus *bus; |
| 382 | int rc; | ||
| 383 | 384 | ||
| 384 | if (slot->type == PHB) { | 385 | BUG_ON(!dn); |
| 385 | rc = set_phb_slot_name(slot); | 386 | bus = rpaphp_find_pci_bus(dn); |
| 386 | if (rc < 0) { | 387 | if (!bus) { |
| 387 | err("%s: failed to set phb slot name\n", __FUNCTION__); | 388 | err("%s: no pci_bus for dn %s\n", __FUNCTION__, dn->full_name); |
| 388 | goto exit_rc; | 389 | goto exit_rc; |
| 389 | } | ||
| 390 | } else { | ||
| 391 | slot->bridge = rpaphp_find_bridge_pdev(slot); | ||
| 392 | if (!slot->bridge) { | ||
| 393 | /* slot being added doesn't have pci_dev yet */ | ||
| 394 | err("%s: no pci_dev for bridge dn %s\n", | ||
| 395 | __FUNCTION__, slot->name); | ||
| 396 | goto exit_rc; | ||
| 397 | } | ||
| 398 | |||
| 399 | bus = slot->bridge->subordinate; | ||
| 400 | if (!bus) | ||
| 401 | goto exit_rc; | ||
| 402 | slot->pci_devs = &bus->devices; | ||
| 403 | |||
| 404 | dbg("%s set slot->name to %s\n", __FUNCTION__, | ||
| 405 | pci_name(slot->bridge)); | ||
| 406 | strcpy(slot->name, pci_name(slot->bridge)); | ||
| 407 | } | 390 | } |
| 408 | 391 | ||
| 392 | slot->bus = bus; | ||
| 393 | slot->pci_devs = &bus->devices; | ||
| 394 | set_slot_name(slot); | ||
| 395 | |||
| 409 | /* find slot's pci_dev if it's not empty */ | 396 | /* find slot's pci_dev if it's not empty */ |
| 410 | if (slot->hotplug_slot->info->adapter_status == EMPTY) { | 397 | if (slot->hotplug_slot->info->adapter_status == EMPTY) { |
| 411 | slot->state = EMPTY; /* slot is empty */ | 398 | slot->state = EMPTY; /* slot is empty */ |
| 412 | } else { | 399 | } else { |
| 413 | /* slot is occupied */ | 400 | /* slot is occupied */ |
| 414 | if (!(slot->dn->child)) { | 401 | if (!dn->child) { |
| 415 | /* non-empty slot has to have child */ | 402 | /* non-empty slot has to have child */ |
| 416 | err("%s: slot[%s]'s device_node doesn't have child for adapter\n", | 403 | err("%s: slot[%s]'s device_node doesn't have child for adapter\n", |
| 417 | __FUNCTION__, slot->name); | 404 | __FUNCTION__, slot->name); |
