diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-10-27 15:48:52 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-11-05 17:31:52 -0500 |
commit | fd6852c8fa060bd45c82a2593e18f933f6c6204f (patch) | |
tree | a0534b189bc6a791e93bce5894f892634aa4ab0c /drivers | |
parent | b5ae5f911d221ad85090d6805ab9ab020f6e4703 (diff) |
powerpc/pci: Fix various pseries PCI hotplug issues
The pseries PCI hotplug code has a number of issues, ranging from
incorrect resource setup to crashes, depending on what is added,
when, whether it contains a bridge, etc etc....
This fixes a whole bunch of these, while actually simplifying the code
a bit, using more generic code in the process and factoring out common
code between adding of a PHB, a slot or a device.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/hotplug/rpadlpar_core.c | 69 |
1 files changed, 30 insertions, 39 deletions
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index 9c2a22fed18b..4e3e0382c16e 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c | |||
@@ -14,6 +14,9 @@ | |||
14 | * as published by the Free Software Foundation; either version | 14 | * as published by the Free Software Foundation; either version |
15 | * 2 of the License, or (at your option) any later version. | 15 | * 2 of the License, or (at your option) any later version. |
16 | */ | 16 | */ |
17 | |||
18 | #undef DEBUG | ||
19 | |||
17 | #include <linux/init.h> | 20 | #include <linux/init.h> |
18 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
19 | #include <linux/string.h> | 22 | #include <linux/string.h> |
@@ -151,20 +154,20 @@ static void dlpar_pci_add_bus(struct device_node *dn) | |||
151 | return; | 154 | return; |
152 | } | 155 | } |
153 | 156 | ||
157 | /* Scan below the new bridge */ | ||
154 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || | 158 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || |
155 | dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) | 159 | dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) |
156 | of_scan_pci_bridge(dn, dev); | 160 | of_scan_pci_bridge(dn, dev); |
157 | 161 | ||
158 | pcibios_fixup_new_pci_devices(dev->subordinate); | ||
159 | |||
160 | /* Claim new bus resources */ | ||
161 | pcibios_claim_one_bus(dev->bus); | ||
162 | |||
163 | /* Map IO space for child bus, which may or may not succeed */ | 162 | /* Map IO space for child bus, which may or may not succeed */ |
164 | pcibios_map_io_space(dev->subordinate); | 163 | pcibios_map_io_space(dev->subordinate); |
165 | 164 | ||
166 | /* Add new devices to global lists. Register in proc, sysfs. */ | 165 | /* Finish adding it : resource allocation, adding devices, etc... |
167 | pci_bus_add_devices(phb->bus); | 166 | * Note that we need to perform the finish pass on the -parent- |
167 | * bus of the EADS bridge so the bridge device itself gets | ||
168 | * properly added | ||
169 | */ | ||
170 | pcibios_finish_adding_to_bus(phb->bus); | ||
168 | } | 171 | } |
169 | 172 | ||
170 | static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) | 173 | static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) |
@@ -203,27 +206,6 @@ static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) | |||
203 | return 0; | 206 | return 0; |
204 | } | 207 | } |
205 | 208 | ||
206 | static int dlpar_remove_root_bus(struct pci_controller *phb) | ||
207 | { | ||
208 | struct pci_bus *phb_bus; | ||
209 | int rc; | ||
210 | |||
211 | phb_bus = phb->bus; | ||
212 | if (!(list_empty(&phb_bus->children) && | ||
213 | list_empty(&phb_bus->devices))) { | ||
214 | return -EBUSY; | ||
215 | } | ||
216 | |||
217 | rc = pcibios_remove_root_bus(phb); | ||
218 | if (rc) | ||
219 | return -EIO; | ||
220 | |||
221 | device_unregister(phb_bus->bridge); | ||
222 | pci_remove_bus(phb_bus); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static int dlpar_remove_phb(char *drc_name, struct device_node *dn) | 209 | static int dlpar_remove_phb(char *drc_name, struct device_node *dn) |
228 | { | 210 | { |
229 | struct slot *slot; | 211 | struct slot *slot; |
@@ -235,18 +217,15 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn) | |||
235 | 217 | ||
236 | /* If pci slot is hotplugable, use hotplug to remove it */ | 218 | /* If pci slot is hotplugable, use hotplug to remove it */ |
237 | slot = find_php_slot(dn); | 219 | slot = find_php_slot(dn); |
238 | if (slot) { | 220 | if (slot && rpaphp_deregister_slot(slot)) { |
239 | if (rpaphp_deregister_slot(slot)) { | 221 | printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", |
240 | printk(KERN_ERR | 222 | __func__, drc_name); |
241 | "%s: unable to remove hotplug slot %s\n", | 223 | return -EIO; |
242 | __func__, drc_name); | ||
243 | return -EIO; | ||
244 | } | ||
245 | } | 224 | } |
246 | 225 | ||
247 | pdn = dn->data; | 226 | pdn = dn->data; |
248 | BUG_ON(!pdn || !pdn->phb); | 227 | BUG_ON(!pdn || !pdn->phb); |
249 | rc = dlpar_remove_root_bus(pdn->phb); | 228 | rc = remove_phb_dynamic(pdn->phb); |
250 | if (rc < 0) | 229 | if (rc < 0) |
251 | return rc; | 230 | return rc; |
252 | 231 | ||
@@ -378,26 +357,38 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) | |||
378 | if (!bus) | 357 | if (!bus) |
379 | return -EINVAL; | 358 | return -EINVAL; |
380 | 359 | ||
381 | /* If pci slot is hotplugable, use hotplug to remove it */ | 360 | pr_debug("PCI: Removing PCI slot below EADS bridge %s\n", |
361 | bus->self ? pci_name(bus->self) : "<!PHB!>"); | ||
362 | |||
382 | slot = find_php_slot(dn); | 363 | slot = find_php_slot(dn); |
383 | if (slot) { | 364 | if (slot) { |
365 | pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n", | ||
366 | pci_domain_nr(bus), bus->number); | ||
367 | |||
384 | if (rpaphp_deregister_slot(slot)) { | 368 | if (rpaphp_deregister_slot(slot)) { |
385 | printk(KERN_ERR | 369 | printk(KERN_ERR |
386 | "%s: unable to remove hotplug slot %s\n", | 370 | "%s: unable to remove hotplug slot %s\n", |
387 | __func__, drc_name); | 371 | __func__, drc_name); |
388 | return -EIO; | 372 | return -EIO; |
389 | } | 373 | } |
390 | } else | 374 | } |
391 | pcibios_remove_pci_devices(bus); | 375 | |
376 | /* Remove all devices below slot */ | ||
377 | pcibios_remove_pci_devices(bus); | ||
392 | 378 | ||
379 | /* Unmap PCI IO space */ | ||
393 | if (pcibios_unmap_io_space(bus)) { | 380 | if (pcibios_unmap_io_space(bus)) { |
394 | printk(KERN_ERR "%s: failed to unmap bus range\n", | 381 | printk(KERN_ERR "%s: failed to unmap bus range\n", |
395 | __func__); | 382 | __func__); |
396 | return -ERANGE; | 383 | return -ERANGE; |
397 | } | 384 | } |
398 | 385 | ||
386 | /* Remove the EADS bridge device itself */ | ||
399 | BUG_ON(!bus->self); | 387 | BUG_ON(!bus->self); |
388 | pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); | ||
389 | eeh_remove_bus_device(bus->self); | ||
400 | pci_remove_bus_device(bus->self); | 390 | pci_remove_bus_device(bus->self); |
391 | |||
401 | return 0; | 392 | return 0; |
402 | } | 393 | } |
403 | 394 | ||