diff options
Diffstat (limited to 'drivers/pci/hotplug/rpaphp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/rpaphp_pci.c | 267 |
1 files changed, 6 insertions, 261 deletions
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c index 396b54b0c847..6f6cbede5135 100644 --- a/drivers/pci/hotplug/rpaphp_pci.c +++ b/drivers/pci/hotplug/rpaphp_pci.c | |||
@@ -32,37 +32,7 @@ | |||
32 | #include "../pci.h" /* for pci_add_new_bus */ | 32 | #include "../pci.h" /* for pci_add_new_bus */ |
33 | #include "rpaphp.h" | 33 | #include "rpaphp.h" |
34 | 34 | ||
35 | static struct pci_bus *find_bus_among_children(struct pci_bus *bus, | 35 | int rpaphp_get_sensor_state(struct slot *slot, int *state) |
36 | struct device_node *dn) | ||
37 | { | ||
38 | struct pci_bus *child = NULL; | ||
39 | struct list_head *tmp; | ||
40 | struct device_node *busdn; | ||
41 | |||
42 | busdn = pci_bus_to_OF_node(bus); | ||
43 | if (busdn == dn) | ||
44 | return bus; | ||
45 | |||
46 | list_for_each(tmp, &bus->children) { | ||
47 | child = find_bus_among_children(pci_bus_b(tmp), dn); | ||
48 | if (child) | ||
49 | break; | ||
50 | } | ||
51 | return child; | ||
52 | } | ||
53 | |||
54 | struct pci_bus *rpaphp_find_pci_bus(struct device_node *dn) | ||
55 | { | ||
56 | struct pci_dn *pdn = dn->data; | ||
57 | |||
58 | if (!pdn || !pdn->phb || !pdn->phb->bus) | ||
59 | return NULL; | ||
60 | |||
61 | return find_bus_among_children(pdn->phb->bus, dn); | ||
62 | } | ||
63 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_bus); | ||
64 | |||
65 | static int rpaphp_get_sensor_state(struct slot *slot, int *state) | ||
66 | { | 36 | { |
67 | int rc; | 37 | int rc; |
68 | int setlevel; | 38 | int setlevel; |
@@ -120,7 +90,7 @@ int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value) | |||
120 | /* config/unconfig adapter */ | 90 | /* config/unconfig adapter */ |
121 | *value = slot->state; | 91 | *value = slot->state; |
122 | } else { | 92 | } else { |
123 | bus = rpaphp_find_pci_bus(slot->dn); | 93 | bus = pcibios_find_pci_bus(slot->dn); |
124 | if (bus && !list_empty(&bus->devices)) | 94 | if (bus && !list_empty(&bus->devices)) |
125 | *value = CONFIGURED; | 95 | *value = CONFIGURED; |
126 | else | 96 | else |
@@ -131,140 +101,6 @@ exit: | |||
131 | return rc; | 101 | return rc; |
132 | } | 102 | } |
133 | 103 | ||
134 | /* Must be called before pci_bus_add_devices */ | ||
135 | void rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) | ||
136 | { | ||
137 | struct pci_dev *dev; | ||
138 | |||
139 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
140 | /* | ||
141 | * Skip already-present devices (which are on the | ||
142 | * global device list.) | ||
143 | */ | ||
144 | if (list_empty(&dev->global_list)) { | ||
145 | int i; | ||
146 | |||
147 | /* Need to setup IOMMU tables */ | ||
148 | ppc_md.iommu_dev_setup(dev); | ||
149 | |||
150 | if(fix_bus) | ||
151 | pcibios_fixup_device_resources(dev, bus); | ||
152 | pci_read_irq_line(dev); | ||
153 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
154 | struct resource *r = &dev->resource[i]; | ||
155 | |||
156 | if (r->parent || !r->start || !r->flags) | ||
157 | continue; | ||
158 | pci_claim_resource(dev, i); | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | static void rpaphp_eeh_add_bus_device(struct pci_bus *bus) | ||
165 | { | ||
166 | struct pci_dev *dev; | ||
167 | |||
168 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
169 | eeh_add_device_late(dev); | ||
170 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
171 | struct pci_bus *subbus = dev->subordinate; | ||
172 | if (subbus) | ||
173 | rpaphp_eeh_add_bus_device (subbus); | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static int rpaphp_pci_config_bridge(struct pci_dev *dev) | ||
179 | { | ||
180 | u8 sec_busno; | ||
181 | struct pci_bus *child_bus; | ||
182 | struct pci_dev *child_dev; | ||
183 | |||
184 | dbg("Enter %s: BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev)); | ||
185 | |||
186 | /* get busno of downstream bus */ | ||
187 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); | ||
188 | |||
189 | /* add to children of PCI bridge dev->bus */ | ||
190 | child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); | ||
191 | if (!child_bus) { | ||
192 | err("%s: could not add second bus\n", __FUNCTION__); | ||
193 | return -EIO; | ||
194 | } | ||
195 | sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); | ||
196 | /* do pci_scan_child_bus */ | ||
197 | pci_scan_child_bus(child_bus); | ||
198 | |||
199 | list_for_each_entry(child_dev, &child_bus->devices, bus_list) { | ||
200 | eeh_add_device_late(child_dev); | ||
201 | } | ||
202 | |||
203 | /* fixup new pci devices without touching bus struct */ | ||
204 | rpaphp_fixup_new_pci_devices(child_bus, 0); | ||
205 | |||
206 | /* Make the discovered devices available */ | ||
207 | pci_bus_add_devices(child_bus); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | void rpaphp_init_new_devs(struct pci_bus *bus) | ||
212 | { | ||
213 | rpaphp_fixup_new_pci_devices(bus, 0); | ||
214 | rpaphp_eeh_add_bus_device(bus); | ||
215 | } | ||
216 | EXPORT_SYMBOL_GPL(rpaphp_init_new_devs); | ||
217 | |||
218 | /***************************************************************************** | ||
219 | rpaphp_pci_config_slot() will configure all devices under the | ||
220 | given slot->dn and return the the first pci_dev. | ||
221 | *****************************************************************************/ | ||
222 | static struct pci_dev * | ||
223 | rpaphp_pci_config_slot(struct pci_bus *bus) | ||
224 | { | ||
225 | struct device_node *dn = pci_bus_to_OF_node(bus); | ||
226 | struct pci_dev *dev = NULL; | ||
227 | int slotno; | ||
228 | int num; | ||
229 | |||
230 | dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name); | ||
231 | if (!dn || !dn->child) | ||
232 | return NULL; | ||
233 | |||
234 | if (_machine == PLATFORM_PSERIES_LPAR) { | ||
235 | of_scan_bus(dn, bus); | ||
236 | if (list_empty(&bus->devices)) { | ||
237 | err("%s: No new device found\n", __FUNCTION__); | ||
238 | return NULL; | ||
239 | } | ||
240 | |||
241 | rpaphp_init_new_devs(bus); | ||
242 | pci_bus_add_devices(bus); | ||
243 | dev = list_entry(&bus->devices, struct pci_dev, bus_list); | ||
244 | } else { | ||
245 | slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); | ||
246 | |||
247 | /* pci_scan_slot should find all children */ | ||
248 | num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); | ||
249 | if (num) { | ||
250 | rpaphp_fixup_new_pci_devices(bus, 1); | ||
251 | pci_bus_add_devices(bus); | ||
252 | } | ||
253 | if (list_empty(&bus->devices)) { | ||
254 | err("%s: No new device found\n", __FUNCTION__); | ||
255 | return NULL; | ||
256 | } | ||
257 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
258 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) | ||
259 | rpaphp_pci_config_bridge(dev); | ||
260 | |||
261 | rpaphp_eeh_add_bus_device(bus); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | return dev; | ||
266 | } | ||
267 | |||
268 | static void print_slot_pci_funcs(struct pci_bus *bus) | 104 | static void print_slot_pci_funcs(struct pci_bus *bus) |
269 | { | 105 | { |
270 | struct device_node *dn; | 106 | struct device_node *dn; |
@@ -280,60 +116,6 @@ static void print_slot_pci_funcs(struct pci_bus *bus) | |||
280 | return; | 116 | return; |
281 | } | 117 | } |
282 | 118 | ||
283 | int rpaphp_config_pci_adapter(struct pci_bus *bus) | ||
284 | { | ||
285 | struct device_node *dn = pci_bus_to_OF_node(bus); | ||
286 | struct pci_dev *dev; | ||
287 | int rc = -ENODEV; | ||
288 | |||
289 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, dn->full_name); | ||
290 | if (!dn) | ||
291 | goto exit; | ||
292 | |||
293 | eeh_add_device_tree_early(dn); | ||
294 | dev = rpaphp_pci_config_slot(bus); | ||
295 | if (!dev) { | ||
296 | err("%s: can't find any devices.\n", __FUNCTION__); | ||
297 | goto exit; | ||
298 | } | ||
299 | print_slot_pci_funcs(bus); | ||
300 | rc = 0; | ||
301 | exit: | ||
302 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); | ||
303 | return rc; | ||
304 | } | ||
305 | EXPORT_SYMBOL_GPL(rpaphp_config_pci_adapter); | ||
306 | |||
307 | static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) | ||
308 | { | ||
309 | eeh_remove_device(dev); | ||
310 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
311 | struct pci_bus *bus = dev->subordinate; | ||
312 | struct list_head *ln; | ||
313 | if (!bus) | ||
314 | return; | ||
315 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { | ||
316 | struct pci_dev *pdev = pci_dev_b(ln); | ||
317 | if (pdev) | ||
318 | rpaphp_eeh_remove_bus_device(pdev); | ||
319 | } | ||
320 | |||
321 | } | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | int rpaphp_unconfig_pci_adapter(struct pci_bus *bus) | ||
326 | { | ||
327 | struct pci_dev *dev, *tmp; | ||
328 | |||
329 | list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { | ||
330 | rpaphp_eeh_remove_bus_device(dev); | ||
331 | pci_remove_bus_device(dev); | ||
332 | } | ||
333 | return 0; | ||
334 | } | ||
335 | EXPORT_SYMBOL_GPL(rpaphp_unconfig_pci_adapter); | ||
336 | |||
337 | static int setup_pci_hotplug_slot_info(struct slot *slot) | 119 | static int setup_pci_hotplug_slot_info(struct slot *slot) |
338 | { | 120 | { |
339 | struct hotplug_slot_info *hotplug_slot_info = slot->hotplug_slot->info; | 121 | struct hotplug_slot_info *hotplug_slot_info = slot->hotplug_slot->info; |
@@ -370,7 +152,7 @@ static int setup_pci_slot(struct slot *slot) | |||
370 | struct pci_bus *bus; | 152 | struct pci_bus *bus; |
371 | 153 | ||
372 | BUG_ON(!dn); | 154 | BUG_ON(!dn); |
373 | bus = rpaphp_find_pci_bus(dn); | 155 | bus = pcibios_find_pci_bus(dn); |
374 | if (!bus) { | 156 | if (!bus) { |
375 | err("%s: no pci_bus for dn %s\n", __FUNCTION__, dn->full_name); | 157 | err("%s: no pci_bus for dn %s\n", __FUNCTION__, dn->full_name); |
376 | goto exit_rc; | 158 | goto exit_rc; |
@@ -395,10 +177,7 @@ static int setup_pci_slot(struct slot *slot) | |||
395 | if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { | 177 | if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { |
396 | dbg("%s CONFIGURING pci adapter in slot[%s]\n", | 178 | dbg("%s CONFIGURING pci adapter in slot[%s]\n", |
397 | __FUNCTION__, slot->name); | 179 | __FUNCTION__, slot->name); |
398 | if (rpaphp_config_pci_adapter(slot->bus)) { | 180 | pcibios_add_pci_devices(slot->bus); |
399 | err("%s: CONFIG pci adapter failed\n", __FUNCTION__); | ||
400 | goto exit_rc; | ||
401 | } | ||
402 | 181 | ||
403 | } else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) { | 182 | } else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) { |
404 | err("%s: slot[%s]'s adapter_status is NOT_VALID.\n", | 183 | err("%s: slot[%s]'s adapter_status is NOT_VALID.\n", |
@@ -420,7 +199,7 @@ exit_rc: | |||
420 | return -EINVAL; | 199 | return -EINVAL; |
421 | } | 200 | } |
422 | 201 | ||
423 | int register_pci_slot(struct slot *slot) | 202 | int rpaphp_register_pci_slot(struct slot *slot) |
424 | { | 203 | { |
425 | int rc = -EINVAL; | 204 | int rc = -EINVAL; |
426 | 205 | ||
@@ -428,42 +207,8 @@ int register_pci_slot(struct slot *slot) | |||
428 | goto exit_rc; | 207 | goto exit_rc; |
429 | if (setup_pci_slot(slot)) | 208 | if (setup_pci_slot(slot)) |
430 | goto exit_rc; | 209 | goto exit_rc; |
431 | rc = register_slot(slot); | 210 | rc = rpaphp_register_slot(slot); |
432 | exit_rc: | 211 | exit_rc: |
433 | return rc; | 212 | return rc; |
434 | } | 213 | } |
435 | 214 | ||
436 | int rpaphp_enable_pci_slot(struct slot *slot) | ||
437 | { | ||
438 | int retval = 0, state; | ||
439 | |||
440 | retval = rpaphp_get_sensor_state(slot, &state); | ||
441 | if (retval) | ||
442 | goto exit; | ||
443 | dbg("%s: sensor state[%d]\n", __FUNCTION__, state); | ||
444 | /* if slot is not empty, enable the adapter */ | ||
445 | if (state == PRESENT) { | ||
446 | dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name); | ||
447 | retval = rpaphp_config_pci_adapter(slot->bus); | ||
448 | if (!retval) { | ||
449 | slot->state = CONFIGURED; | ||
450 | info("%s: devices in slot[%s] configured\n", | ||
451 | __FUNCTION__, slot->name); | ||
452 | } else { | ||
453 | slot->state = NOT_CONFIGURED; | ||
454 | dbg("%s: no pci_dev struct for adapter in slot[%s]\n", | ||
455 | __FUNCTION__, slot->name); | ||
456 | } | ||
457 | } else if (state == EMPTY) { | ||
458 | dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name); | ||
459 | slot->state = EMPTY; | ||
460 | } else { | ||
461 | err("%s: slot[%s] is in invalid state\n", __FUNCTION__, | ||
462 | slot->name); | ||
463 | slot->state = NOT_VALID; | ||
464 | retval = -EINVAL; | ||
465 | } | ||
466 | exit: | ||
467 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | ||
468 | return retval; | ||
469 | } | ||