diff options
Diffstat (limited to 'drivers/pci/hotplug/fakephp.c')
-rw-r--r-- | drivers/pci/hotplug/fakephp.c | 86 |
1 files changed, 30 insertions, 56 deletions
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index 7e9a827c2687..40337a06c18a 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c | |||
@@ -66,6 +66,7 @@ struct dummy_slot { | |||
66 | struct pci_dev *dev; | 66 | struct pci_dev *dev; |
67 | struct work_struct remove_work; | 67 | struct work_struct remove_work; |
68 | unsigned long removed; | 68 | unsigned long removed; |
69 | char name[8]; | ||
69 | }; | 70 | }; |
70 | 71 | ||
71 | static int debug; | 72 | static int debug; |
@@ -100,6 +101,7 @@ static int add_slot(struct pci_dev *dev) | |||
100 | struct dummy_slot *dslot; | 101 | struct dummy_slot *dslot; |
101 | struct hotplug_slot *slot; | 102 | struct hotplug_slot *slot; |
102 | int retval = -ENOMEM; | 103 | int retval = -ENOMEM; |
104 | static int count = 1; | ||
103 | 105 | ||
104 | slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | 106 | slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); |
105 | if (!slot) | 107 | if (!slot) |
@@ -113,18 +115,18 @@ static int add_slot(struct pci_dev *dev) | |||
113 | slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; | 115 | slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; |
114 | slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; | 116 | slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; |
115 | 117 | ||
116 | slot->name = &dev->dev.bus_id[0]; | ||
117 | dbg("slot->name = %s\n", slot->name); | ||
118 | |||
119 | dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL); | 118 | dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL); |
120 | if (!dslot) | 119 | if (!dslot) |
121 | goto error_info; | 120 | goto error_info; |
122 | 121 | ||
122 | slot->name = dslot->name; | ||
123 | snprintf(slot->name, sizeof(dslot->name), "fake%d", count++); | ||
124 | dbg("slot->name = %s\n", slot->name); | ||
123 | slot->ops = &dummy_hotplug_slot_ops; | 125 | slot->ops = &dummy_hotplug_slot_ops; |
124 | slot->release = &dummy_release; | 126 | slot->release = &dummy_release; |
125 | slot->private = dslot; | 127 | slot->private = dslot; |
126 | 128 | ||
127 | retval = pci_hp_register(slot); | 129 | retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn)); |
128 | if (retval) { | 130 | if (retval) { |
129 | err("pci_hp_register failed with error %d\n", retval); | 131 | err("pci_hp_register failed with error %d\n", retval); |
130 | goto error_dslot; | 132 | goto error_dslot; |
@@ -148,17 +150,17 @@ error: | |||
148 | static int __init pci_scan_buses(void) | 150 | static int __init pci_scan_buses(void) |
149 | { | 151 | { |
150 | struct pci_dev *dev = NULL; | 152 | struct pci_dev *dev = NULL; |
151 | int retval = 0; | 153 | int lastslot = 0; |
152 | 154 | ||
153 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | 155 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { |
154 | retval = add_slot(dev); | 156 | if (PCI_FUNC(dev->devfn) > 0 && |
155 | if (retval) { | 157 | lastslot == PCI_SLOT(dev->devfn)) |
156 | pci_dev_put(dev); | 158 | continue; |
157 | break; | 159 | lastslot = PCI_SLOT(dev->devfn); |
158 | } | 160 | add_slot(dev); |
159 | } | 161 | } |
160 | 162 | ||
161 | return retval; | 163 | return 0; |
162 | } | 164 | } |
163 | 165 | ||
164 | static void remove_slot(struct dummy_slot *dslot) | 166 | static void remove_slot(struct dummy_slot *dslot) |
@@ -296,23 +298,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) | |||
296 | return 0; | 298 | return 0; |
297 | } | 299 | } |
298 | 300 | ||
299 | /* find the hotplug_slot for the pci_dev */ | ||
300 | static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev) | ||
301 | { | ||
302 | struct dummy_slot *dslot; | ||
303 | |||
304 | list_for_each_entry(dslot, &slot_list, node) { | ||
305 | if (dslot->dev == dev) | ||
306 | return dslot->slot; | ||
307 | } | ||
308 | return NULL; | ||
309 | } | ||
310 | |||
311 | |||
312 | static int disable_slot(struct hotplug_slot *slot) | 301 | static int disable_slot(struct hotplug_slot *slot) |
313 | { | 302 | { |
314 | struct dummy_slot *dslot; | 303 | struct dummy_slot *dslot; |
315 | struct hotplug_slot *hslot; | ||
316 | struct pci_dev *dev; | 304 | struct pci_dev *dev; |
317 | int func; | 305 | int func; |
318 | 306 | ||
@@ -322,41 +310,27 @@ static int disable_slot(struct hotplug_slot *slot) | |||
322 | 310 | ||
323 | dbg("%s - physical_slot = %s\n", __func__, slot->name); | 311 | dbg("%s - physical_slot = %s\n", __func__, slot->name); |
324 | 312 | ||
325 | /* don't disable bridged devices just yet, we can't handle them easily... */ | 313 | for (func = 7; func >= 0; func--) { |
326 | if (dslot->dev->subordinate) { | 314 | dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func); |
327 | err("Can't remove PCI devices with other PCI devices behind it yet.\n"); | 315 | if (!dev) |
328 | return -ENODEV; | 316 | continue; |
329 | } | 317 | |
330 | if (test_and_set_bit(0, &dslot->removed)) { | 318 | if (test_and_set_bit(0, &dslot->removed)) { |
331 | dbg("Slot already scheduled for removal\n"); | 319 | dbg("Slot already scheduled for removal\n"); |
332 | return -ENODEV; | 320 | return -ENODEV; |
333 | } | ||
334 | /* search for subfunctions and disable them first */ | ||
335 | if (!(dslot->dev->devfn & 7)) { | ||
336 | for (func = 1; func < 8; func++) { | ||
337 | dev = pci_get_slot(dslot->dev->bus, | ||
338 | dslot->dev->devfn + func); | ||
339 | if (dev) { | ||
340 | hslot = get_slot_from_dev(dev); | ||
341 | if (hslot) | ||
342 | disable_slot(hslot); | ||
343 | else { | ||
344 | err("Hotplug slot not found for subfunction of PCI device\n"); | ||
345 | return -ENODEV; | ||
346 | } | ||
347 | pci_dev_put(dev); | ||
348 | } else | ||
349 | dbg("No device in slot found\n"); | ||
350 | } | 321 | } |
351 | } | ||
352 | 322 | ||
353 | /* remove the device from the pci core */ | 323 | /* queue work item to blow away this sysfs entry and other |
354 | pci_remove_bus_device(dslot->dev); | 324 | * parts. |
325 | */ | ||
326 | INIT_WORK(&dslot->remove_work, remove_slot_worker); | ||
327 | queue_work(dummyphp_wq, &dslot->remove_work); | ||
355 | 328 | ||
356 | /* queue work item to blow away this sysfs entry and other parts. */ | 329 | /* blow away this sysfs entry and other parts. */ |
357 | INIT_WORK(&dslot->remove_work, remove_slot_worker); | 330 | remove_slot(dslot); |
358 | queue_work(dummyphp_wq, &dslot->remove_work); | ||
359 | 331 | ||
332 | pci_dev_put(dev); | ||
333 | } | ||
360 | return 0; | 334 | return 0; |
361 | } | 335 | } |
362 | 336 | ||