diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/eeh.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_dev.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_pe.c | 120 |
4 files changed, 129 insertions, 2 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 248b3d99112f..7b9c7d65ee2d 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h | |||
@@ -164,6 +164,8 @@ static inline void eeh_unlock(void) | |||
164 | */ | 164 | */ |
165 | #define EEH_MAX_ALLOWED_FREEZES 5 | 165 | #define EEH_MAX_ALLOWED_FREEZES 5 |
166 | 166 | ||
167 | int __devinit eeh_phb_pe_create(struct pci_controller *phb); | ||
168 | |||
167 | void * __devinit eeh_dev_init(struct device_node *dn, void *data); | 169 | void * __devinit eeh_dev_init(struct device_node *dn, void *data); |
168 | void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb); | 170 | void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb); |
169 | int __init eeh_ops_register(struct eeh_ops *ops); | 171 | int __init eeh_ops_register(struct eeh_ops *ops); |
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index c222189f5bb2..890622b87c8f 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -6,8 +6,9 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ | |||
6 | firmware.o power.o dlpar.o mobility.o | 6 | firmware.o power.o dlpar.o mobility.o |
7 | obj-$(CONFIG_SMP) += smp.o | 7 | obj-$(CONFIG_SMP) += smp.o |
8 | obj-$(CONFIG_SCANLOG) += scanlog.o | 8 | obj-$(CONFIG_SCANLOG) += scanlog.o |
9 | obj-$(CONFIG_EEH) += eeh.o eeh_dev.o eeh_cache.o eeh_driver.o \ | 9 | obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \ |
10 | eeh_event.o eeh_sysfs.o eeh_pseries.o | 10 | eeh_driver.o eeh_event.o eeh_sysfs.o \ |
11 | eeh_pseries.o | ||
11 | obj-$(CONFIG_KEXEC) += kexec.o | 12 | obj-$(CONFIG_KEXEC) += kexec.o |
12 | obj-$(CONFIG_PCI) += pci.o pci_dlpar.o | 13 | obj-$(CONFIG_PCI) += pci.o pci_dlpar.o |
13 | obj-$(CONFIG_PSERIES_MSI) += msi.o | 14 | obj-$(CONFIG_PSERIES_MSI) += msi.o |
diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c index a0cee3a29c97..66442341d3a6 100644 --- a/arch/powerpc/platforms/pseries/eeh_dev.c +++ b/arch/powerpc/platforms/pseries/eeh_dev.c | |||
@@ -65,6 +65,7 @@ void * __devinit eeh_dev_init(struct device_node *dn, void *data) | |||
65 | PCI_DN(dn)->edev = edev; | 65 | PCI_DN(dn)->edev = edev; |
66 | edev->dn = dn; | 66 | edev->dn = dn; |
67 | edev->phb = phb; | 67 | edev->phb = phb; |
68 | INIT_LIST_HEAD(&edev->list); | ||
68 | 69 | ||
69 | return NULL; | 70 | return NULL; |
70 | } | 71 | } |
@@ -80,6 +81,9 @@ void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb) | |||
80 | { | 81 | { |
81 | struct device_node *dn = phb->dn; | 82 | struct device_node *dn = phb->dn; |
82 | 83 | ||
84 | /* EEH PE for PHB */ | ||
85 | eeh_phb_pe_create(phb); | ||
86 | |||
83 | /* EEH device for PHB */ | 87 | /* EEH device for PHB */ |
84 | eeh_dev_init(dn, phb); | 88 | eeh_dev_init(dn, phb); |
85 | 89 | ||
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c new file mode 100644 index 000000000000..7dee7dc83522 --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_pe.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * The file intends to implement PE based on the information from | ||
3 | * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device. | ||
4 | * All the PEs should be organized as hierarchy tree. The first level | ||
5 | * of the tree will be associated to existing PHBs since the particular | ||
6 | * PE is only meaningful in one PHB domain. | ||
7 | * | ||
8 | * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include <linux/export.h> | ||
26 | #include <linux/gfp.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/string.h> | ||
31 | |||
32 | #include <asm/pci-bridge.h> | ||
33 | #include <asm/ppc-pci.h> | ||
34 | |||
35 | static LIST_HEAD(eeh_phb_pe); | ||
36 | |||
37 | /** | ||
38 | * eeh_pe_alloc - Allocate PE | ||
39 | * @phb: PCI controller | ||
40 | * @type: PE type | ||
41 | * | ||
42 | * Allocate PE instance dynamically. | ||
43 | */ | ||
44 | static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type) | ||
45 | { | ||
46 | struct eeh_pe *pe; | ||
47 | |||
48 | /* Allocate PHB PE */ | ||
49 | pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL); | ||
50 | if (!pe) return NULL; | ||
51 | |||
52 | /* Initialize PHB PE */ | ||
53 | pe->type = type; | ||
54 | pe->phb = phb; | ||
55 | INIT_LIST_HEAD(&pe->child_list); | ||
56 | INIT_LIST_HEAD(&pe->child); | ||
57 | INIT_LIST_HEAD(&pe->edevs); | ||
58 | |||
59 | return pe; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * eeh_phb_pe_create - Create PHB PE | ||
64 | * @phb: PCI controller | ||
65 | * | ||
66 | * The function should be called while the PHB is detected during | ||
67 | * system boot or PCI hotplug in order to create PHB PE. | ||
68 | */ | ||
69 | int __devinit eeh_phb_pe_create(struct pci_controller *phb) | ||
70 | { | ||
71 | struct eeh_pe *pe; | ||
72 | |||
73 | /* Allocate PHB PE */ | ||
74 | pe = eeh_pe_alloc(phb, EEH_PE_PHB); | ||
75 | if (!pe) { | ||
76 | pr_err("%s: out of memory!\n", __func__); | ||
77 | return -ENOMEM; | ||
78 | } | ||
79 | |||
80 | /* Put it into the list */ | ||
81 | eeh_lock(); | ||
82 | list_add_tail(&pe->child, &eeh_phb_pe); | ||
83 | eeh_unlock(); | ||
84 | |||
85 | pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB | ||
92 | * @phb: PCI controller | ||
93 | * | ||
94 | * The overall PEs form hierarchy tree. The first layer of the | ||
95 | * hierarchy tree is composed of PHB PEs. The function is used | ||
96 | * to retrieve the corresponding PHB PE according to the given PHB. | ||
97 | */ | ||
98 | static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb) | ||
99 | { | ||
100 | struct eeh_pe *pe; | ||
101 | |||
102 | eeh_lock(); | ||
103 | |||
104 | list_for_each_entry(pe, &eeh_phb_pe, child) { | ||
105 | /* | ||
106 | * Actually, we needn't check the type since | ||
107 | * the PE for PHB has been determined when that | ||
108 | * was created. | ||
109 | */ | ||
110 | if (pe->type == EEH_PE_PHB && | ||
111 | pe->phb == phb) { | ||
112 | eeh_unlock(); | ||
113 | return pe; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | eeh_unlock(); | ||
118 | |||
119 | return NULL; | ||
120 | } | ||