diff options
-rw-r--r-- | arch/powerpc/include/asm/eeh.h | 32 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 53 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_pseries.c | 183 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/setup.c | 1 |
5 files changed, 270 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 232887721ff4..0666c52b8f14 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h | |||
@@ -31,6 +31,26 @@ struct device_node; | |||
31 | 31 | ||
32 | #ifdef CONFIG_EEH | 32 | #ifdef CONFIG_EEH |
33 | 33 | ||
34 | /* | ||
35 | * The struct is used to trace the registered EEH operation | ||
36 | * callback functions. Actually, those operation callback | ||
37 | * functions are heavily platform dependent. That means the | ||
38 | * platform should register its own EEH operation callback | ||
39 | * functions before any EEH further operations. | ||
40 | */ | ||
41 | struct eeh_ops { | ||
42 | char *name; | ||
43 | int (*init)(void); | ||
44 | int (*set_option)(struct device_node *dn, int option); | ||
45 | int (*get_pe_addr)(struct device_node *dn); | ||
46 | int (*get_state)(struct device_node *dn, int *state); | ||
47 | int (*reset)(struct device_node *dn, int option); | ||
48 | int (*wait_state)(struct device_node *dn, int max_wait); | ||
49 | int (*get_log)(struct device_node *dn, int severity, char *drv_log, unsigned long len); | ||
50 | int (*configure_bridge)(struct device_node *dn); | ||
51 | }; | ||
52 | |||
53 | extern struct eeh_ops *eeh_ops; | ||
34 | extern int eeh_subsystem_enabled; | 54 | extern int eeh_subsystem_enabled; |
35 | 55 | ||
36 | /* Values for eeh_mode bits in device_node */ | 56 | /* Values for eeh_mode bits in device_node */ |
@@ -47,6 +67,11 @@ extern int eeh_subsystem_enabled; | |||
47 | #define EEH_MAX_ALLOWED_FREEZES 5 | 67 | #define EEH_MAX_ALLOWED_FREEZES 5 |
48 | 68 | ||
49 | void __init eeh_init(void); | 69 | void __init eeh_init(void); |
70 | #ifdef CONFIG_PPC_PSERIES | ||
71 | int __init eeh_pseries_init(void); | ||
72 | #endif | ||
73 | int __init eeh_ops_register(struct eeh_ops *ops); | ||
74 | int __exit eeh_ops_unregister(const char *name); | ||
50 | unsigned long eeh_check_failure(const volatile void __iomem *token, | 75 | unsigned long eeh_check_failure(const volatile void __iomem *token, |
51 | unsigned long val); | 76 | unsigned long val); |
52 | int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev); | 77 | int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev); |
@@ -73,6 +98,13 @@ void eeh_remove_bus_device(struct pci_dev *); | |||
73 | #else /* !CONFIG_EEH */ | 98 | #else /* !CONFIG_EEH */ |
74 | static inline void eeh_init(void) { } | 99 | static inline void eeh_init(void) { } |
75 | 100 | ||
101 | #ifdef CONFIG_PPC_PSERIES | ||
102 | static inline int eeh_pseries_init(void) | ||
103 | { | ||
104 | return 0; | ||
105 | } | ||
106 | #endif /* CONFIG_PPC_PSERIES */ | ||
107 | |||
76 | static inline unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val) | 108 | static inline unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val) |
77 | { | 109 | { |
78 | return val; | 110 | return val; |
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 67b3e6e0cf3f..ee873cf4fe46 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -6,7 +6,7 @@ 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_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o | 9 | obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o eeh_pseries.o |
10 | obj-$(CONFIG_KEXEC) += kexec.o | 10 | obj-$(CONFIG_KEXEC) += kexec.o |
11 | obj-$(CONFIG_PCI) += pci.o pci_dlpar.o | 11 | obj-$(CONFIG_PCI) += pci.o pci_dlpar.o |
12 | obj-$(CONFIG_PSERIES_MSI) += msi.o | 12 | obj-$(CONFIG_PSERIES_MSI) += msi.o |
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index fa885891e1c3..b0e3fb0b32a5 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -97,6 +97,9 @@ static int ibm_get_config_addr_info2; | |||
97 | static int ibm_configure_bridge; | 97 | static int ibm_configure_bridge; |
98 | static int ibm_configure_pe; | 98 | static int ibm_configure_pe; |
99 | 99 | ||
100 | /* Platform dependent EEH operations */ | ||
101 | struct eeh_ops *eeh_ops = NULL; | ||
102 | |||
100 | int eeh_subsystem_enabled; | 103 | int eeh_subsystem_enabled; |
101 | EXPORT_SYMBOL(eeh_subsystem_enabled); | 104 | EXPORT_SYMBOL(eeh_subsystem_enabled); |
102 | 105 | ||
@@ -1208,6 +1211,56 @@ static void *eeh_early_enable(struct device_node *dn, void *data) | |||
1208 | } | 1211 | } |
1209 | 1212 | ||
1210 | /** | 1213 | /** |
1214 | * eeh_ops_register - Register platform dependent EEH operations | ||
1215 | * @ops: platform dependent EEH operations | ||
1216 | * | ||
1217 | * Register the platform dependent EEH operation callback | ||
1218 | * functions. The platform should call this function before | ||
1219 | * any other EEH operations. | ||
1220 | */ | ||
1221 | int __init eeh_ops_register(struct eeh_ops *ops) | ||
1222 | { | ||
1223 | if (!ops->name) { | ||
1224 | pr_warning("%s: Invalid EEH ops name for %p\n", | ||
1225 | __func__, ops); | ||
1226 | return -EINVAL; | ||
1227 | } | ||
1228 | |||
1229 | if (eeh_ops && eeh_ops != ops) { | ||
1230 | pr_warning("%s: EEH ops of platform %s already existing (%s)\n", | ||
1231 | __func__, eeh_ops->name, ops->name); | ||
1232 | return -EEXIST; | ||
1233 | } | ||
1234 | |||
1235 | eeh_ops = ops; | ||
1236 | |||
1237 | return 0; | ||
1238 | } | ||
1239 | |||
1240 | /** | ||
1241 | * eeh_ops_unregister - Unreigster platform dependent EEH operations | ||
1242 | * @name: name of EEH platform operations | ||
1243 | * | ||
1244 | * Unregister the platform dependent EEH operation callback | ||
1245 | * functions. | ||
1246 | */ | ||
1247 | int __exit eeh_ops_unregister(const char *name) | ||
1248 | { | ||
1249 | if (!name || !strlen(name)) { | ||
1250 | pr_warning("%s: Invalid EEH ops name\n", | ||
1251 | __func__); | ||
1252 | return -EINVAL; | ||
1253 | } | ||
1254 | |||
1255 | if (eeh_ops && !strcmp(eeh_ops->name, name)) { | ||
1256 | eeh_ops = NULL; | ||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | return -EEXIST; | ||
1261 | } | ||
1262 | |||
1263 | /** | ||
1211 | * eeh_init - EEH initialization | 1264 | * eeh_init - EEH initialization |
1212 | * | 1265 | * |
1213 | * Initialize EEH by trying to enable it for all of the adapters in the system. | 1266 | * Initialize EEH by trying to enable it for all of the adapters in the system. |
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c new file mode 100644 index 000000000000..61a9050ba969 --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * The file intends to implement the platform dependent EEH operations on pseries. | ||
3 | * Actually, the pseries platform is built based on RTAS heavily. That means the | ||
4 | * pseries platform dependent EEH operations will be built on RTAS calls. The functions | ||
5 | * are devired from arch/powerpc/platforms/pseries/eeh.c and necessary cleanup has | ||
6 | * been done. | ||
7 | * | ||
8 | * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2011. | ||
9 | * Copyright IBM Corporation 2001, 2005, 2006 | ||
10 | * Copyright Dave Engebretsen & Todd Inglett 2001 | ||
11 | * Copyright Linas Vepstas 2005, 2006 | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
26 | */ | ||
27 | |||
28 | #include <linux/atomic.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/export.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/list.h> | ||
33 | #include <linux/of.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/proc_fs.h> | ||
36 | #include <linux/rbtree.h> | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/seq_file.h> | ||
39 | #include <linux/spinlock.h> | ||
40 | |||
41 | #include <asm/eeh.h> | ||
42 | #include <asm/eeh_event.h> | ||
43 | #include <asm/io.h> | ||
44 | #include <asm/machdep.h> | ||
45 | #include <asm/ppc-pci.h> | ||
46 | #include <asm/rtas.h> | ||
47 | |||
48 | /** | ||
49 | * pseries_eeh_init - EEH platform dependent initialization | ||
50 | * | ||
51 | * EEH platform dependent initialization on pseries. | ||
52 | */ | ||
53 | static int pseries_eeh_init(void) | ||
54 | { | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * pseries_eeh_set_option - Initialize EEH or MMIO/DMA reenable | ||
60 | * @dn: device node | ||
61 | * @option: operation to be issued | ||
62 | * | ||
63 | * The function is used to control the EEH functionality globally. | ||
64 | * Currently, following options are support according to PAPR: | ||
65 | * Enable EEH, Disable EEH, Enable MMIO and Enable DMA | ||
66 | */ | ||
67 | static int pseries_eeh_set_option(struct device_node *dn, int option) | ||
68 | { | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * pseries_eeh_get_pe_addr - Retrieve PE address | ||
74 | * @dn: device node | ||
75 | * | ||
76 | * Retrieve the assocated PE address. Actually, there're 2 RTAS | ||
77 | * function calls dedicated for the purpose. We need implement | ||
78 | * it through the new function and then the old one. Besides, | ||
79 | * you should make sure the config address is figured out from | ||
80 | * FDT node before calling the function. | ||
81 | * | ||
82 | * It's notable that zero'ed return value means invalid PE config | ||
83 | * address. | ||
84 | */ | ||
85 | static int pseries_eeh_get_pe_addr(struct device_node *dn) | ||
86 | { | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * pseries_eeh_get_state - Retrieve PE state | ||
92 | * @dn: PE associated device node | ||
93 | * @state: return value | ||
94 | * | ||
95 | * Retrieve the state of the specified PE. On RTAS compliant | ||
96 | * pseries platform, there already has one dedicated RTAS function | ||
97 | * for the purpose. It's notable that the associated PE config address | ||
98 | * might be ready when calling the function. Therefore, endeavour to | ||
99 | * use the PE config address if possible. Further more, there're 2 | ||
100 | * RTAS calls for the purpose, we need to try the new one and back | ||
101 | * to the old one if the new one couldn't work properly. | ||
102 | */ | ||
103 | static int pseries_eeh_get_state(struct device_node *dn, int *state) | ||
104 | { | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * pseries_eeh_reset - Reset the specified PE | ||
110 | * @dn: PE associated device node | ||
111 | * @option: reset option | ||
112 | * | ||
113 | * Reset the specified PE | ||
114 | */ | ||
115 | static int pseries_eeh_reset(struct device_node *dn, int option) | ||
116 | { | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * pseries_eeh_wait_state - Wait for PE state | ||
122 | * @dn: PE associated device node | ||
123 | * @max_wait: maximal period in microsecond | ||
124 | * | ||
125 | * Wait for the state of associated PE. It might take some time | ||
126 | * to retrieve the PE's state. | ||
127 | */ | ||
128 | static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) | ||
129 | { | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * pseries_eeh_get_log - Retrieve error log | ||
135 | * @dn: device node | ||
136 | * @severity: temporary or permanent error log | ||
137 | * @drv_log: driver log to be combined with retrieved error log | ||
138 | * @len: length of driver log | ||
139 | * | ||
140 | * Retrieve the temporary or permanent error from the PE. | ||
141 | * Actually, the error will be retrieved through the dedicated | ||
142 | * RTAS call. | ||
143 | */ | ||
144 | static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) | ||
145 | { | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE | ||
151 | * @dn: PE associated device node | ||
152 | * | ||
153 | * The function will be called to reconfigure the bridges included | ||
154 | * in the specified PE so that the mulfunctional PE would be recovered | ||
155 | * again. | ||
156 | */ | ||
157 | static int pseries_eeh_configure_bridge(struct device_node *dn) | ||
158 | { | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static struct eeh_ops pseries_eeh_ops = { | ||
163 | .name = "pseries", | ||
164 | .init = pseries_eeh_init, | ||
165 | .set_option = pseries_eeh_set_option, | ||
166 | .get_pe_addr = pseries_eeh_get_pe_addr, | ||
167 | .get_state = pseries_eeh_get_state, | ||
168 | .reset = pseries_eeh_reset, | ||
169 | .wait_state = pseries_eeh_wait_state, | ||
170 | .get_log = pseries_eeh_get_log, | ||
171 | .configure_bridge = pseries_eeh_configure_bridge | ||
172 | }; | ||
173 | |||
174 | /** | ||
175 | * eeh_pseries_init - Register platform dependent EEH operations | ||
176 | * | ||
177 | * EEH initialization on pseries platform. This function should be | ||
178 | * called before any EEH related functions. | ||
179 | */ | ||
180 | int __init eeh_pseries_init(void) | ||
181 | { | ||
182 | return eeh_ops_register(&pseries_eeh_ops); | ||
183 | } | ||
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index d928412cfb32..62b827626ca6 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c | |||
@@ -381,6 +381,7 @@ static void __init pSeries_setup_arch(void) | |||
381 | 381 | ||
382 | /* Find and initialize PCI host bridges */ | 382 | /* Find and initialize PCI host bridges */ |
383 | init_pci_config_tokens(); | 383 | init_pci_config_tokens(); |
384 | eeh_pseries_init(); | ||
384 | find_and_init_phbs(); | 385 | find_and_init_phbs(); |
385 | pSeries_reconfig_notifier_register(&pci_dn_reconfig_nb); | 386 | pSeries_reconfig_notifier_register(&pci_dn_reconfig_nb); |
386 | eeh_init(); | 387 | eeh_init(); |