aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2008-10-27 15:48:52 -0400
committerPaul Mackerras <paulus@samba.org>2008-11-05 17:31:52 -0500
commitfd6852c8fa060bd45c82a2593e18f933f6c6204f (patch)
treea0534b189bc6a791e93bce5894f892634aa4ab0c /drivers
parentb5ae5f911d221ad85090d6805ab9ab020f6e4703 (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.c69
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
170static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) 173static 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
206static 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
227static int dlpar_remove_phb(char *drc_name, struct device_node *dn) 209static 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