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 /arch/powerpc/platforms/pseries | |
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 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/pci_dlpar.c | 163 |
1 files changed, 81 insertions, 82 deletions
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); | ||