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 | |
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>
-rw-r--r-- | arch/powerpc/include/asm/pci-bridge.h | 3 | ||||
-rw-r--r-- | arch/powerpc/include/asm/pci.h | 5 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci-common.c | 41 | ||||
-rw-r--r-- | arch/powerpc/kernel/rtas_pci.c | 48 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pci_dlpar.c | 163 | ||||
-rw-r--r-- | drivers/pci/hotplug/rpadlpar_core.c | 69 |
6 files changed, 150 insertions, 179 deletions
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 2dfa8a3d8c76..fa8b3b724438 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h | |||
@@ -241,9 +241,6 @@ extern void pcibios_remove_pci_devices(struct pci_bus *bus); | |||
241 | 241 | ||
242 | /** Discover new pci devices under this bus, and add them */ | 242 | /** Discover new pci devices under this bus, and add them */ |
243 | extern void pcibios_add_pci_devices(struct pci_bus *bus); | 243 | extern void pcibios_add_pci_devices(struct pci_bus *bus); |
244 | extern void pcibios_fixup_new_pci_devices(struct pci_bus *bus); | ||
245 | |||
246 | extern int pcibios_remove_root_bus(struct pci_controller *phb); | ||
247 | 244 | ||
248 | static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) | 245 | static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) |
249 | { | 246 | { |
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 32e03e6d25c5..1c721a632d8e 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h | |||
@@ -204,15 +204,14 @@ static inline struct resource *pcibios_select_root(struct pci_dev *pdev, | |||
204 | return root; | 204 | return root; |
205 | } | 205 | } |
206 | 206 | ||
207 | extern void pcibios_setup_new_device(struct pci_dev *dev); | ||
208 | |||
209 | extern void pcibios_claim_one_bus(struct pci_bus *b); | 207 | extern void pcibios_claim_one_bus(struct pci_bus *b); |
210 | 208 | ||
211 | extern void pcibios_allocate_bus_resources(struct pci_bus *bus); | 209 | extern void pcibios_finish_adding_to_bus(struct pci_bus *bus); |
212 | 210 | ||
213 | extern void pcibios_resource_survey(void); | 211 | extern void pcibios_resource_survey(void); |
214 | 212 | ||
215 | extern struct pci_controller *init_phb_dynamic(struct device_node *dn); | 213 | extern struct pci_controller *init_phb_dynamic(struct device_node *dn); |
214 | extern int remove_phb_dynamic(struct pci_controller *phb); | ||
216 | 215 | ||
217 | extern struct pci_dev *of_create_pci_dev(struct device_node *node, | 216 | extern struct pci_dev *of_create_pci_dev(struct device_node *node, |
218 | struct pci_bus *bus, int devfn); | 217 | struct pci_bus *bus, int devfn); |
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index f965397a6105..f3fd7eb90a7b 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c | |||
@@ -203,7 +203,7 @@ char __devinit *pcibios_setup(char *str) | |||
203 | return str; | 203 | return str; |
204 | } | 204 | } |
205 | 205 | ||
206 | void __devinit pcibios_setup_new_device(struct pci_dev *dev) | 206 | static void __devinit pcibios_setup_new_device(struct pci_dev *dev) |
207 | { | 207 | { |
208 | struct dev_archdata *sd = &dev->dev.archdata; | 208 | struct dev_archdata *sd = &dev->dev.archdata; |
209 | 209 | ||
@@ -221,7 +221,6 @@ void __devinit pcibios_setup_new_device(struct pci_dev *dev) | |||
221 | if (ppc_md.pci_dma_dev_setup) | 221 | if (ppc_md.pci_dma_dev_setup) |
222 | ppc_md.pci_dma_dev_setup(dev); | 222 | ppc_md.pci_dma_dev_setup(dev); |
223 | } | 223 | } |
224 | EXPORT_SYMBOL(pcibios_setup_new_device); | ||
225 | 224 | ||
226 | /* | 225 | /* |
227 | * Reads the interrupt pin to determine if interrupt is use by card. | 226 | * Reads the interrupt pin to determine if interrupt is use by card. |
@@ -1397,9 +1396,10 @@ void __init pcibios_resource_survey(void) | |||
1397 | 1396 | ||
1398 | #ifdef CONFIG_HOTPLUG | 1397 | #ifdef CONFIG_HOTPLUG |
1399 | 1398 | ||
1400 | /* This is used by the pSeries hotplug driver to allocate resource | 1399 | /* This is used by the PCI hotplug driver to allocate resource |
1401 | * of newly plugged busses. We can try to consolidate with the | 1400 | * of newly plugged busses. We can try to consolidate with the |
1402 | * rest of the code later, for now, keep it as-is | 1401 | * rest of the code later, for now, keep it as-is as our main |
1402 | * resource allocation function doesn't deal with sub-trees yet. | ||
1403 | */ | 1403 | */ |
1404 | void __devinit pcibios_claim_one_bus(struct pci_bus *bus) | 1404 | void __devinit pcibios_claim_one_bus(struct pci_bus *bus) |
1405 | { | 1405 | { |
@@ -1414,6 +1414,14 @@ void __devinit pcibios_claim_one_bus(struct pci_bus *bus) | |||
1414 | 1414 | ||
1415 | if (r->parent || !r->start || !r->flags) | 1415 | if (r->parent || !r->start || !r->flags) |
1416 | continue; | 1416 | continue; |
1417 | |||
1418 | pr_debug("PCI: Claiming %s: " | ||
1419 | "Resource %d: %016llx..%016llx [%x]\n", | ||
1420 | pci_name(dev), i, | ||
1421 | (unsigned long long)r->start, | ||
1422 | (unsigned long long)r->end, | ||
1423 | (unsigned int)r->flags); | ||
1424 | |||
1417 | pci_claim_resource(dev, i); | 1425 | pci_claim_resource(dev, i); |
1418 | } | 1426 | } |
1419 | } | 1427 | } |
@@ -1422,6 +1430,31 @@ void __devinit pcibios_claim_one_bus(struct pci_bus *bus) | |||
1422 | pcibios_claim_one_bus(child_bus); | 1430 | pcibios_claim_one_bus(child_bus); |
1423 | } | 1431 | } |
1424 | EXPORT_SYMBOL_GPL(pcibios_claim_one_bus); | 1432 | EXPORT_SYMBOL_GPL(pcibios_claim_one_bus); |
1433 | |||
1434 | |||
1435 | /* pcibios_finish_adding_to_bus | ||
1436 | * | ||
1437 | * This is to be called by the hotplug code after devices have been | ||
1438 | * added to a bus, this include calling it for a PHB that is just | ||
1439 | * being added | ||
1440 | */ | ||
1441 | void pcibios_finish_adding_to_bus(struct pci_bus *bus) | ||
1442 | { | ||
1443 | pr_debug("PCI: Finishing adding to hotplug bus %04x:%02x\n", | ||
1444 | pci_domain_nr(bus), bus->number); | ||
1445 | |||
1446 | /* Allocate bus and devices resources */ | ||
1447 | pcibios_allocate_bus_resources(bus); | ||
1448 | pcibios_claim_one_bus(bus); | ||
1449 | |||
1450 | /* Add new devices to global lists. Register in proc, sysfs. */ | ||
1451 | pci_bus_add_devices(bus); | ||
1452 | |||
1453 | /* Fixup EEH */ | ||
1454 | eeh_add_device_tree_late(bus); | ||
1455 | } | ||
1456 | EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); | ||
1457 | |||
1425 | #endif /* CONFIG_HOTPLUG */ | 1458 | #endif /* CONFIG_HOTPLUG */ |
1426 | 1459 | ||
1427 | int pcibios_enable_device(struct pci_dev *dev, int mask) | 1460 | int pcibios_enable_device(struct pci_dev *dev, int mask) |
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index 589a2797eac2..8869001ab5d7 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c | |||
@@ -301,51 +301,3 @@ void __init find_and_init_phbs(void) | |||
301 | #endif /* CONFIG_PPC32 */ | 301 | #endif /* CONFIG_PPC32 */ |
302 | } | 302 | } |
303 | } | 303 | } |
304 | |||
305 | /* RPA-specific bits for removing PHBs */ | ||
306 | int pcibios_remove_root_bus(struct pci_controller *phb) | ||
307 | { | ||
308 | struct pci_bus *b = phb->bus; | ||
309 | struct resource *res; | ||
310 | int rc, i; | ||
311 | |||
312 | res = b->resource[0]; | ||
313 | if (!res->flags) { | ||
314 | printk(KERN_ERR "%s: no IO resource for PHB %s\n", __func__, | ||
315 | b->name); | ||
316 | return 1; | ||
317 | } | ||
318 | |||
319 | rc = pcibios_unmap_io_space(b); | ||
320 | if (rc) { | ||
321 | printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", | ||
322 | __func__, b->name); | ||
323 | return 1; | ||
324 | } | ||
325 | |||
326 | if (release_resource(res)) { | ||
327 | printk(KERN_ERR "%s: failed to release IO on bus %s\n", | ||
328 | __func__, b->name); | ||
329 | return 1; | ||
330 | } | ||
331 | |||
332 | for (i = 1; i < 3; ++i) { | ||
333 | res = b->resource[i]; | ||
334 | if (!res->flags && i == 0) { | ||
335 | printk(KERN_ERR "%s: no MEM resource for PHB %s\n", | ||
336 | __func__, b->name); | ||
337 | return 1; | ||
338 | } | ||
339 | if (res->flags && release_resource(res)) { | ||
340 | printk(KERN_ERR | ||
341 | "%s: failed to release IO %d on bus %s\n", | ||
342 | __func__, i, b->name); | ||
343 | return 1; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | pcibios_free_controller(phb); | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | EXPORT_SYMBOL(pcibios_remove_root_bus); | ||
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 7190493e9bdc..5e1ed3d60ee5 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c | |||
@@ -25,6 +25,8 @@ | |||
25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | #undef DEBUG | ||
29 | |||
28 | #include <linux/pci.h> | 30 | #include <linux/pci.h> |
29 | #include <asm/pci-bridge.h> | 31 | #include <asm/pci-bridge.h> |
30 | #include <asm/ppc-pci.h> | 32 | #include <asm/ppc-pci.h> |
@@ -69,74 +71,25 @@ EXPORT_SYMBOL_GPL(pcibios_find_pci_bus); | |||
69 | * Remove all of the PCI devices under this bus both from the | 71 | * Remove all of the PCI devices under this bus both from the |
70 | * linux pci device tree, and from the powerpc EEH address cache. | 72 | * linux pci device tree, and from the powerpc EEH address cache. |
71 | */ | 73 | */ |
72 | void | 74 | void pcibios_remove_pci_devices(struct pci_bus *bus) |
73 | pcibios_remove_pci_devices(struct pci_bus *bus) | ||
74 | { | 75 | { |
75 | struct pci_dev *dev, *tmp; | 76 | struct pci_dev *dev, *tmp; |
77 | struct pci_bus *child_bus; | ||
78 | |||
79 | /* First go down child busses */ | ||
80 | list_for_each_entry(child_bus, &bus->children, node) | ||
81 | pcibios_remove_pci_devices(child_bus); | ||
76 | 82 | ||
83 | pr_debug("PCI: Removing devices on bus %04x:%02x\n", | ||
84 | pci_domain_nr(bus), bus->number); | ||
77 | list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { | 85 | list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { |
86 | pr_debug(" * Removing %s...\n", pci_name(dev)); | ||
78 | eeh_remove_bus_device(dev); | 87 | eeh_remove_bus_device(dev); |
79 | pci_remove_bus_device(dev); | 88 | pci_remove_bus_device(dev); |
80 | } | 89 | } |
81 | } | 90 | } |
82 | EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); | 91 | EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); |
83 | 92 | ||
84 | /* Must be called before pci_bus_add_devices */ | ||
85 | void | ||
86 | pcibios_fixup_new_pci_devices(struct pci_bus *bus) | ||
87 | { | ||
88 | struct pci_dev *dev; | ||
89 | |||
90 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
91 | /* Skip already-added devices */ | ||
92 | if (!dev->is_added) { | ||
93 | int i; | ||
94 | |||
95 | /* Fill device archdata and setup iommu table */ | ||
96 | pcibios_setup_new_device(dev); | ||
97 | |||
98 | pci_read_irq_line(dev); | ||
99 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
100 | struct resource *r = &dev->resource[i]; | ||
101 | |||
102 | if (r->parent || !r->start || !r->flags) | ||
103 | continue; | ||
104 | pci_claim_resource(dev, i); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices); | ||
110 | |||
111 | static int | ||
112 | pcibios_pci_config_bridge(struct pci_dev *dev) | ||
113 | { | ||
114 | u8 sec_busno; | ||
115 | struct pci_bus *child_bus; | ||
116 | |||
117 | /* Get busno of downstream bus */ | ||
118 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); | ||
119 | |||
120 | /* Add to children of PCI bridge dev->bus */ | ||
121 | child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); | ||
122 | if (!child_bus) { | ||
123 | printk (KERN_ERR "%s: could not add second bus\n", __func__); | ||
124 | return -EIO; | ||
125 | } | ||
126 | sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); | ||
127 | |||
128 | pci_scan_child_bus(child_bus); | ||
129 | |||
130 | /* Fixup new pci devices */ | ||
131 | pcibios_fixup_new_pci_devices(child_bus); | ||
132 | |||
133 | /* Make the discovered devices available */ | ||
134 | pci_bus_add_devices(child_bus); | ||
135 | |||
136 | eeh_add_device_tree_late(child_bus); | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | /** | 93 | /** |
141 | * pcibios_add_pci_devices - adds new pci devices to bus | 94 | * pcibios_add_pci_devices - adds new pci devices to bus |
142 | * | 95 | * |
@@ -147,10 +100,9 @@ pcibios_pci_config_bridge(struct pci_dev *dev) | |||
147 | * is how this routine differs from other, similar pcibios | 100 | * is how this routine differs from other, similar pcibios |
148 | * routines.) | 101 | * routines.) |
149 | */ | 102 | */ |
150 | void | 103 | void pcibios_add_pci_devices(struct pci_bus * bus) |
151 | pcibios_add_pci_devices(struct pci_bus * bus) | ||
152 | { | 104 | { |
153 | int slotno, num, mode; | 105 | int slotno, num, mode, pass, max; |
154 | struct pci_dev *dev; | 106 | struct pci_dev *dev; |
155 | struct device_node *dn = pci_bus_to_OF_node(bus); | 107 | struct device_node *dn = pci_bus_to_OF_node(bus); |
156 | 108 | ||
@@ -162,26 +114,23 @@ pcibios_add_pci_devices(struct pci_bus * bus) | |||
162 | 114 | ||
163 | if (mode == PCI_PROBE_DEVTREE) { | 115 | if (mode == PCI_PROBE_DEVTREE) { |
164 | /* use ofdt-based probe */ | 116 | /* use ofdt-based probe */ |
165 | of_scan_bus(dn, bus); | 117 | of_rescan_bus(dn, bus); |
166 | if (!list_empty(&bus->devices)) { | ||
167 | pcibios_fixup_new_pci_devices(bus); | ||
168 | pci_bus_add_devices(bus); | ||
169 | eeh_add_device_tree_late(bus); | ||
170 | } | ||
171 | } else if (mode == PCI_PROBE_NORMAL) { | 118 | } else if (mode == PCI_PROBE_NORMAL) { |
172 | /* use legacy probe */ | 119 | /* use legacy probe */ |
173 | slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); | 120 | slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); |
174 | num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); | 121 | num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); |
175 | if (num) { | 122 | if (!num) |
176 | pcibios_fixup_new_pci_devices(bus); | 123 | return; |
177 | pci_bus_add_devices(bus); | 124 | pcibios_setup_bus_devices(bus); |
178 | eeh_add_device_tree_late(bus); | 125 | max = bus->secondary; |
126 | for (pass=0; pass < 2; pass++) | ||
127 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
128 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || | ||
129 | dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) | ||
130 | max = pci_scan_bridge(bus, dev, max, pass); | ||
179 | } | 131 | } |
180 | |||
181 | list_for_each_entry(dev, &bus->devices, bus_list) | ||
182 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) | ||
183 | pcibios_pci_config_bridge(dev); | ||
184 | } | 132 | } |
133 | pcibios_finish_adding_to_bus(bus); | ||
185 | } | 134 | } |
186 | EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); | 135 | EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); |
187 | 136 | ||
@@ -190,6 +139,8 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) | |||
190 | struct pci_controller *phb; | 139 | struct pci_controller *phb; |
191 | int primary; | 140 | int primary; |
192 | 141 | ||
142 | pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name); | ||
143 | |||
193 | primary = list_empty(&hose_list); | 144 | primary = list_empty(&hose_list); |
194 | phb = pcibios_alloc_controller(dn); | 145 | phb = pcibios_alloc_controller(dn); |
195 | if (!phb) | 146 | if (!phb) |
@@ -203,11 +154,59 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) | |||
203 | eeh_add_device_tree_early(dn); | 154 | eeh_add_device_tree_early(dn); |
204 | 155 | ||
205 | scan_phb(phb); | 156 | scan_phb(phb); |
206 | pcibios_allocate_bus_resources(phb->bus); | 157 | pcibios_finish_adding_to_bus(phb->bus); |
207 | pcibios_fixup_new_pci_devices(phb->bus); | ||
208 | pci_bus_add_devices(phb->bus); | ||
209 | eeh_add_device_tree_late(phb->bus); | ||
210 | 158 | ||
211 | return phb; | 159 | return phb; |
212 | } | 160 | } |
213 | EXPORT_SYMBOL_GPL(init_phb_dynamic); | 161 | EXPORT_SYMBOL_GPL(init_phb_dynamic); |
162 | |||
163 | /* RPA-specific bits for removing PHBs */ | ||
164 | int remove_phb_dynamic(struct pci_controller *phb) | ||
165 | { | ||
166 | struct pci_bus *b = phb->bus; | ||
167 | struct resource *res; | ||
168 | int rc, i; | ||
169 | |||
170 | pr_debug("PCI: Removing PHB %04x:%02x... \n", | ||
171 | pci_domain_nr(b), b->number); | ||
172 | |||
173 | /* We cannot to remove a root bus that has children */ | ||
174 | if (!(list_empty(&b->children) && list_empty(&b->devices))) | ||
175 | return -EBUSY; | ||
176 | |||
177 | /* We -know- there aren't any child devices anymore at this stage | ||
178 | * and thus, we can safely unmap the IO space as it's not in use | ||
179 | */ | ||
180 | res = &phb->io_resource; | ||
181 | if (res->flags & IORESOURCE_IO) { | ||
182 | rc = pcibios_unmap_io_space(b); | ||
183 | if (rc) { | ||
184 | printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", | ||
185 | __func__, b->name); | ||
186 | return 1; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /* Unregister the bridge device from sysfs and remove the PCI bus */ | ||
191 | device_unregister(b->bridge); | ||
192 | phb->bus = NULL; | ||
193 | pci_remove_bus(b); | ||
194 | |||
195 | /* Now release the IO resource */ | ||
196 | if (res->flags & IORESOURCE_IO) | ||
197 | release_resource(res); | ||
198 | |||
199 | /* Release memory resources */ | ||
200 | for (i = 0; i < 3; ++i) { | ||
201 | res = &phb->mem_resources[i]; | ||
202 | if (!(res->flags & IORESOURCE_MEM)) | ||
203 | continue; | ||
204 | release_resource(res); | ||
205 | } | ||
206 | |||
207 | /* Free pci_controller data structure */ | ||
208 | pcibios_free_controller(phb); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | EXPORT_SYMBOL_GPL(remove_phb_dynamic); | ||
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 | ||