aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorGavin Shan <shangw@linux.vnet.ibm.com>2012-09-07 18:44:09 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-09-09 19:35:31 -0400
commit9b84348c92a122f3ccecda04083ada620312aa53 (patch)
tree9dc1c17f168117b62f90d1466af647f0ffce7488 /arch/powerpc
parent22f4ab123f10e1862579785c73d9e60fa24afd4f (diff)
powerpc/eeh: Create PEs duing EEH initialization
The patch creates PEs and associated the newly created PEs with it parent/silbing as well as EEH devices. It would become more straight to trace EEH errors and recover them accordingly. Once the EEH functionality on one PCI IOA has been enabled, we tries to create PE against it. If there's existing PE, to which the current PCI IOA should be attached, the existing PE will be converted from "device" type to "bus" type accordingly. Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/include/asm/eeh.h1
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c6
-rw-r--r--arch/powerpc/platforms/pseries/eeh_pe.c80
3 files changed, 87 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 1cc1388d6201..6b13790fdadb 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -166,6 +166,7 @@ static inline void eeh_unlock(void)
166 166
167typedef void *(*eeh_traverse_func)(void *data, void *flag); 167typedef void *(*eeh_traverse_func)(void *data, void *flag);
168int __devinit eeh_phb_pe_create(struct pci_controller *phb); 168int __devinit eeh_phb_pe_create(struct pci_controller *phb);
169int eeh_add_to_parent_pe(struct eeh_dev *edev);
169 170
170void * __devinit eeh_dev_init(struct device_node *dn, void *data); 171void * __devinit eeh_dev_init(struct device_node *dn, void *data);
171void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb); 172void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb);
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 0ba7e3b84672..8f2149061846 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -895,6 +895,8 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
895 eeh_subsystem_enabled = 1; 895 eeh_subsystem_enabled = 1;
896 edev->mode |= EEH_MODE_SUPPORTED; 896 edev->mode |= EEH_MODE_SUPPORTED;
897 897
898 eeh_add_to_parent_pe(edev);
899
898 pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n", 900 pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n",
899 dn->full_name, edev->config_addr, 901 dn->full_name, edev->config_addr,
900 edev->pe_config_addr); 902 edev->pe_config_addr);
@@ -908,6 +910,10 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
908 /* Parent supports EEH. */ 910 /* Parent supports EEH. */
909 edev->mode |= EEH_MODE_SUPPORTED; 911 edev->mode |= EEH_MODE_SUPPORTED;
910 edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr; 912 edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr;
913 edev->pe_config_addr = of_node_to_eeh_dev(dn->parent)->pe_config_addr;
914
915 eeh_add_to_parent_pe(edev);
916
911 return NULL; 917 return NULL;
912 } 918 }
913 } 919 }
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
index f74b350c392d..1d632739d28e 100644
--- a/arch/powerpc/platforms/pseries/eeh_pe.c
+++ b/arch/powerpc/platforms/pseries/eeh_pe.c
@@ -261,3 +261,83 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
261 261
262 return NULL; 262 return NULL;
263} 263}
264
265/**
266 * eeh_add_to_parent_pe - Add EEH device to parent PE
267 * @edev: EEH device
268 *
269 * Add EEH device to the parent PE. If the parent PE already
270 * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
271 * we have to create new PE to hold the EEH device and the new
272 * PE will be linked to its parent PE as well.
273 */
274int eeh_add_to_parent_pe(struct eeh_dev *edev)
275{
276 struct eeh_pe *pe, *parent;
277
278 /*
279 * Search the PE has been existing or not according
280 * to the PE address. If that has been existing, the
281 * PE should be composed of PCI bus and its subordinate
282 * components.
283 */
284 pe = eeh_pe_get(edev);
285 if (pe) {
286 if (!edev->pe_config_addr) {
287 pr_err("%s: PE with addr 0x%x already exists\n",
288 __func__, edev->config_addr);
289 return -EEXIST;
290 }
291
292 /* Mark the PE as type of PCI bus */
293 pe->type = EEH_PE_BUS;
294 edev->pe = pe;
295
296 /* Put the edev to PE */
297 list_add_tail(&edev->list, &pe->edevs);
298 pr_debug("EEH: Add %s to Bus PE#%x\n",
299 edev->dn->full_name, pe->addr);
300
301 return 0;
302 }
303
304 /* Create a new EEH PE */
305 pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
306 if (!pe) {
307 pr_err("%s: out of memory!\n", __func__);
308 return -ENOMEM;
309 }
310 pe->addr = edev->pe_config_addr;
311 pe->config_addr = edev->config_addr;
312
313 /*
314 * Put the new EEH PE into hierarchy tree. If the parent
315 * can't be found, the newly created PE will be attached
316 * to PHB directly. Otherwise, we have to associate the
317 * PE with its parent.
318 */
319 parent = eeh_pe_get_parent(edev);
320 if (!parent) {
321 parent = eeh_phb_pe_get(edev->phb);
322 if (!parent) {
323 pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
324 __func__, edev->phb->global_number);
325 edev->pe = NULL;
326 kfree(pe);
327 return -EEXIST;
328 }
329 }
330 pe->parent = parent;
331
332 /*
333 * Put the newly created PE into the child list and
334 * link the EEH device accordingly.
335 */
336 list_add_tail(&pe->child, &parent->child_list);
337 list_add_tail(&edev->list, &pe->edevs);
338 edev->pe = pe;
339 pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
340 edev->dn->full_name, pe->addr, pe->parent->addr);
341
342 return 0;
343}