Commit | Line | Data |
---|---|---|
aa1e6374 GS |
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 | |
027dfac6 | 5 | * are derived from arch/powerpc/platforms/pseries/eeh.c and necessary cleanup has |
aa1e6374 GS |
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 | ||
e2af155c GS |
48 | /* RTAS tokens */ |
49 | static int ibm_set_eeh_option; | |
50 | static int ibm_set_slot_reset; | |
51 | static int ibm_read_slot_reset_state; | |
52 | static int ibm_read_slot_reset_state2; | |
53 | static int ibm_slot_error_detail; | |
54 | static int ibm_get_config_addr_info; | |
55 | static int ibm_get_config_addr_info2; | |
e2af155c GS |
56 | static int ibm_configure_pe; |
57 | ||
8d633291 GS |
58 | /* |
59 | * Buffer for reporting slot-error-detail rtas calls. Its here | |
60 | * in BSS, and not dynamically alloced, so that it ends up in | |
61 | * RMO where RTAS can access it. | |
62 | */ | |
63 | static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX]; | |
64 | static DEFINE_SPINLOCK(slot_errbuf_lock); | |
65 | static int eeh_error_buf_size; | |
66 | ||
aa1e6374 GS |
67 | /** |
68 | * pseries_eeh_init - EEH platform dependent initialization | |
69 | * | |
70 | * EEH platform dependent initialization on pseries. | |
71 | */ | |
72 | static int pseries_eeh_init(void) | |
73 | { | |
e2af155c GS |
74 | /* figure out EEH RTAS function call tokens */ |
75 | ibm_set_eeh_option = rtas_token("ibm,set-eeh-option"); | |
76 | ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); | |
77 | ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2"); | |
78 | ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); | |
79 | ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); | |
80 | ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2"); | |
81 | ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info"); | |
82 | ibm_configure_pe = rtas_token("ibm,configure-pe"); | |
bd000b82 RC |
83 | |
84 | /* | |
85 | * ibm,configure-pe and ibm,configure-bridge have the same semantics, | |
86 | * however ibm,configure-pe can be faster. If we can't find | |
87 | * ibm,configure-pe then fall back to using ibm,configure-bridge. | |
88 | */ | |
89 | if (ibm_configure_pe == RTAS_UNKNOWN_SERVICE) | |
90 | ibm_configure_pe = rtas_token("ibm,configure-bridge"); | |
e2af155c | 91 | |
b8b3de22 GS |
92 | /* |
93 | * Necessary sanity check. We needn't check "get-config-addr-info" | |
94 | * and its variant since the old firmware probably support address | |
95 | * of domain/bus/slot/function for EEH RTAS operations. | |
96 | */ | |
4ba5a0fc GS |
97 | if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE || |
98 | ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE || | |
99 | (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE && | |
100 | ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) || | |
101 | ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE || | |
bd000b82 | 102 | ibm_configure_pe == RTAS_UNKNOWN_SERVICE) { |
4ba5a0fc | 103 | pr_info("EEH functionality not supported\n"); |
e2af155c GS |
104 | return -EINVAL; |
105 | } | |
106 | ||
8d633291 GS |
107 | /* Initialize error log lock and size */ |
108 | spin_lock_init(&slot_errbuf_lock); | |
109 | eeh_error_buf_size = rtas_token("rtas-error-log-max"); | |
110 | if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { | |
4ba5a0fc | 111 | pr_info("%s: unknown EEH error log size\n", |
8d633291 GS |
112 | __func__); |
113 | eeh_error_buf_size = 1024; | |
114 | } else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) { | |
4ba5a0fc | 115 | pr_info("%s: EEH error log size %d exceeds the maximal %d\n", |
8d633291 GS |
116 | __func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX); |
117 | eeh_error_buf_size = RTAS_ERROR_LOG_MAX; | |
118 | } | |
119 | ||
d7bb8862 | 120 | /* Set EEH probe mode */ |
dc561fb9 | 121 | eeh_add_flag(EEH_PROBE_MODE_DEVTREE | EEH_ENABLE_IO_FOR_LOG); |
d7bb8862 | 122 | |
aa1e6374 GS |
123 | return 0; |
124 | } | |
125 | ||
ff57b454 | 126 | static int pseries_eeh_cap_start(struct pci_dn *pdn) |
4b83bd45 | 127 | { |
4b83bd45 GS |
128 | u32 status; |
129 | ||
130 | if (!pdn) | |
131 | return 0; | |
132 | ||
133 | rtas_read_config(pdn, PCI_STATUS, 2, &status); | |
134 | if (!(status & PCI_STATUS_CAP_LIST)) | |
135 | return 0; | |
136 | ||
137 | return PCI_CAPABILITY_LIST; | |
138 | } | |
139 | ||
140 | ||
ff57b454 | 141 | static int pseries_eeh_find_cap(struct pci_dn *pdn, int cap) |
4b83bd45 | 142 | { |
ff57b454 | 143 | int pos = pseries_eeh_cap_start(pdn); |
4b83bd45 GS |
144 | int cnt = 48; /* Maximal number of capabilities */ |
145 | u32 id; | |
146 | ||
147 | if (!pos) | |
148 | return 0; | |
149 | ||
150 | while (cnt--) { | |
151 | rtas_read_config(pdn, pos, 1, &pos); | |
152 | if (pos < 0x40) | |
153 | break; | |
154 | pos &= ~3; | |
155 | rtas_read_config(pdn, pos + PCI_CAP_LIST_ID, 1, &id); | |
156 | if (id == 0xff) | |
157 | break; | |
158 | if (id == cap) | |
159 | return pos; | |
160 | pos += PCI_CAP_LIST_NEXT; | |
161 | } | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
ff57b454 | 166 | static int pseries_eeh_find_ecap(struct pci_dn *pdn, int cap) |
2a18dfc6 | 167 | { |
ff57b454 | 168 | struct eeh_dev *edev = pdn_to_eeh_dev(pdn); |
2a18dfc6 GS |
169 | u32 header; |
170 | int pos = 256; | |
171 | int ttl = (4096 - 256) / 8; | |
172 | ||
173 | if (!edev || !edev->pcie_cap) | |
174 | return 0; | |
175 | if (rtas_read_config(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL) | |
176 | return 0; | |
177 | else if (!header) | |
178 | return 0; | |
179 | ||
180 | while (ttl-- > 0) { | |
181 | if (PCI_EXT_CAP_ID(header) == cap && pos) | |
182 | return pos; | |
183 | ||
184 | pos = PCI_EXT_CAP_NEXT(header); | |
185 | if (pos < 256) | |
186 | break; | |
187 | ||
188 | if (rtas_read_config(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL) | |
189 | break; | |
190 | } | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
d7bb8862 | 195 | /** |
ff57b454 GS |
196 | * pseries_eeh_probe - EEH probe on the given device |
197 | * @pdn: PCI device node | |
198 | * @data: Unused | |
d7bb8862 GS |
199 | * |
200 | * When EEH module is installed during system boot, all PCI devices | |
201 | * are checked one by one to see if it supports EEH. The function | |
202 | * is introduced for the purpose. | |
203 | */ | |
ff57b454 | 204 | static void *pseries_eeh_probe(struct pci_dn *pdn, void *data) |
d7bb8862 GS |
205 | { |
206 | struct eeh_dev *edev; | |
207 | struct eeh_pe pe; | |
4b83bd45 | 208 | u32 pcie_flags; |
d7bb8862 GS |
209 | int enable = 0; |
210 | int ret; | |
211 | ||
212 | /* Retrieve OF node and eeh device */ | |
ff57b454 GS |
213 | edev = pdn_to_eeh_dev(pdn); |
214 | if (!edev || edev->pe) | |
d7bb8862 GS |
215 | return NULL; |
216 | ||
ff57b454 GS |
217 | /* Check class/vendor/device IDs */ |
218 | if (!pdn->vendor_id || !pdn->device_id || !pdn->class_code) | |
d7bb8862 GS |
219 | return NULL; |
220 | ||
ff57b454 GS |
221 | /* Skip for PCI-ISA bridge */ |
222 | if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA) | |
223 | return NULL; | |
b91da2d4 | 224 | |
4b83bd45 GS |
225 | /* |
226 | * Update class code and mode of eeh device. We need | |
227 | * correctly reflects that current device is root port | |
228 | * or PCIe switch downstream port. | |
229 | */ | |
ff57b454 GS |
230 | edev->class_code = pdn->class_code; |
231 | edev->pcix_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); | |
232 | edev->pcie_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_EXP); | |
233 | edev->aer_cap = pseries_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR); | |
ab55d218 | 234 | edev->mode &= 0xFFFFFF00; |
4b83bd45 GS |
235 | if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) { |
236 | edev->mode |= EEH_DEV_BRIDGE; | |
237 | if (edev->pcie_cap) { | |
238 | rtas_read_config(pdn, edev->pcie_cap + PCI_EXP_FLAGS, | |
239 | 2, &pcie_flags); | |
240 | pcie_flags = (pcie_flags & PCI_EXP_FLAGS_TYPE) >> 4; | |
241 | if (pcie_flags == PCI_EXP_TYPE_ROOT_PORT) | |
242 | edev->mode |= EEH_DEV_ROOT_PORT; | |
243 | else if (pcie_flags == PCI_EXP_TYPE_DOWNSTREAM) | |
244 | edev->mode |= EEH_DEV_DS_PORT; | |
245 | } | |
246 | } | |
d7bb8862 | 247 | |
d7bb8862 GS |
248 | /* Initialize the fake PE */ |
249 | memset(&pe, 0, sizeof(struct eeh_pe)); | |
69672bd7 | 250 | pe.phb = pdn->phb; |
ff57b454 | 251 | pe.config_addr = (pdn->busno << 16) | (pdn->devfn << 8); |
d7bb8862 GS |
252 | |
253 | /* Enable EEH on the device */ | |
254 | ret = eeh_ops->set_option(&pe, EEH_OPT_ENABLE); | |
255 | if (!ret) { | |
d7bb8862 GS |
256 | /* Retrieve PE address */ |
257 | edev->pe_config_addr = eeh_ops->get_pe_addr(&pe); | |
258 | pe.addr = edev->pe_config_addr; | |
259 | ||
260 | /* Some older systems (Power4) allow the ibm,set-eeh-option | |
261 | * call to succeed even on nodes where EEH is not supported. | |
262 | * Verify support explicitly. | |
263 | */ | |
264 | ret = eeh_ops->get_state(&pe, NULL); | |
265 | if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT) | |
266 | enable = 1; | |
267 | ||
268 | if (enable) { | |
05b1721d | 269 | eeh_add_flag(EEH_ENABLED); |
d7bb8862 GS |
270 | eeh_add_to_parent_pe(edev); |
271 | ||
1f52f176 | 272 | pr_debug("%s: EEH enabled on %02x:%02x.%01x PHB#%x-PE#%x\n", |
ff57b454 GS |
273 | __func__, pdn->busno, PCI_SLOT(pdn->devfn), |
274 | PCI_FUNC(pdn->devfn), pe.phb->global_number, | |
275 | pe.addr); | |
276 | } else if (pdn->parent && pdn_to_eeh_dev(pdn->parent) && | |
277 | (pdn_to_eeh_dev(pdn->parent))->pe) { | |
d7bb8862 GS |
278 | /* This device doesn't support EEH, but it may have an |
279 | * EEH parent, in which case we mark it as supported. | |
280 | */ | |
ff57b454 | 281 | edev->pe_config_addr = pdn_to_eeh_dev(pdn->parent)->pe_config_addr; |
d7bb8862 GS |
282 | eeh_add_to_parent_pe(edev); |
283 | } | |
284 | } | |
285 | ||
286 | /* Save memory bars */ | |
287 | eeh_save_bars(edev); | |
288 | ||
289 | return NULL; | |
290 | } | |
291 | ||
aa1e6374 GS |
292 | /** |
293 | * pseries_eeh_set_option - Initialize EEH or MMIO/DMA reenable | |
371a395d | 294 | * @pe: EEH PE |
aa1e6374 GS |
295 | * @option: operation to be issued |
296 | * | |
297 | * The function is used to control the EEH functionality globally. | |
298 | * Currently, following options are support according to PAPR: | |
299 | * Enable EEH, Disable EEH, Enable MMIO and Enable DMA | |
300 | */ | |
371a395d | 301 | static int pseries_eeh_set_option(struct eeh_pe *pe, int option) |
aa1e6374 | 302 | { |
8fb8f709 | 303 | int ret = 0; |
8fb8f709 GS |
304 | int config_addr; |
305 | ||
8fb8f709 GS |
306 | /* |
307 | * When we're enabling or disabling EEH functioality on | |
308 | * the particular PE, the PE config address is possibly | |
309 | * unavailable. Therefore, we have to figure it out from | |
310 | * the FDT node. | |
311 | */ | |
312 | switch (option) { | |
313 | case EEH_OPT_DISABLE: | |
314 | case EEH_OPT_ENABLE: | |
8fb8f709 GS |
315 | case EEH_OPT_THAW_MMIO: |
316 | case EEH_OPT_THAW_DMA: | |
371a395d GS |
317 | config_addr = pe->config_addr; |
318 | if (pe->addr) | |
319 | config_addr = pe->addr; | |
8fb8f709 | 320 | break; |
0d5ee520 GS |
321 | case EEH_OPT_FREEZE_PE: |
322 | /* Not support */ | |
323 | return 0; | |
8fb8f709 GS |
324 | default: |
325 | pr_err("%s: Invalid option %d\n", | |
326 | __func__, option); | |
327 | return -EINVAL; | |
328 | } | |
329 | ||
330 | ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL, | |
371a395d GS |
331 | config_addr, BUID_HI(pe->phb->buid), |
332 | BUID_LO(pe->phb->buid), option); | |
8fb8f709 GS |
333 | |
334 | return ret; | |
aa1e6374 GS |
335 | } |
336 | ||
337 | /** | |
338 | * pseries_eeh_get_pe_addr - Retrieve PE address | |
371a395d | 339 | * @pe: EEH PE |
aa1e6374 GS |
340 | * |
341 | * Retrieve the assocated PE address. Actually, there're 2 RTAS | |
342 | * function calls dedicated for the purpose. We need implement | |
343 | * it through the new function and then the old one. Besides, | |
344 | * you should make sure the config address is figured out from | |
345 | * FDT node before calling the function. | |
346 | * | |
347 | * It's notable that zero'ed return value means invalid PE config | |
348 | * address. | |
349 | */ | |
371a395d | 350 | static int pseries_eeh_get_pe_addr(struct eeh_pe *pe) |
aa1e6374 | 351 | { |
c8c29b38 GS |
352 | int ret = 0; |
353 | int rets[3]; | |
354 | ||
c8c29b38 GS |
355 | if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { |
356 | /* | |
357 | * First of all, we need to make sure there has one PE | |
358 | * associated with the device. Otherwise, PE address is | |
359 | * meaningless. | |
360 | */ | |
361 | ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, | |
371a395d GS |
362 | pe->config_addr, BUID_HI(pe->phb->buid), |
363 | BUID_LO(pe->phb->buid), 1); | |
c8c29b38 GS |
364 | if (ret || (rets[0] == 0)) |
365 | return 0; | |
366 | ||
367 | /* Retrieve the associated PE config address */ | |
368 | ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, | |
371a395d GS |
369 | pe->config_addr, BUID_HI(pe->phb->buid), |
370 | BUID_LO(pe->phb->buid), 0); | |
c8c29b38 | 371 | if (ret) { |
1f52f176 | 372 | pr_warn("%s: Failed to get address for PHB#%x-PE#%x\n", |
371a395d | 373 | __func__, pe->phb->global_number, pe->config_addr); |
c8c29b38 GS |
374 | return 0; |
375 | } | |
376 | ||
377 | return rets[0]; | |
378 | } | |
379 | ||
380 | if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { | |
381 | ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, | |
371a395d GS |
382 | pe->config_addr, BUID_HI(pe->phb->buid), |
383 | BUID_LO(pe->phb->buid), 0); | |
c8c29b38 | 384 | if (ret) { |
1f52f176 | 385 | pr_warn("%s: Failed to get address for PHB#%x-PE#%x\n", |
371a395d | 386 | __func__, pe->phb->global_number, pe->config_addr); |
c8c29b38 GS |
387 | return 0; |
388 | } | |
389 | ||
390 | return rets[0]; | |
391 | } | |
392 | ||
393 | return ret; | |
aa1e6374 GS |
394 | } |
395 | ||
396 | /** | |
397 | * pseries_eeh_get_state - Retrieve PE state | |
371a395d | 398 | * @pe: EEH PE |
aa1e6374 GS |
399 | * @state: return value |
400 | * | |
401 | * Retrieve the state of the specified PE. On RTAS compliant | |
402 | * pseries platform, there already has one dedicated RTAS function | |
403 | * for the purpose. It's notable that the associated PE config address | |
404 | * might be ready when calling the function. Therefore, endeavour to | |
405 | * use the PE config address if possible. Further more, there're 2 | |
406 | * RTAS calls for the purpose, we need to try the new one and back | |
407 | * to the old one if the new one couldn't work properly. | |
408 | */ | |
371a395d | 409 | static int pseries_eeh_get_state(struct eeh_pe *pe, int *state) |
aa1e6374 | 410 | { |
eb594a47 GS |
411 | int config_addr; |
412 | int ret; | |
413 | int rets[4]; | |
414 | int result; | |
415 | ||
416 | /* Figure out PE config address if possible */ | |
371a395d GS |
417 | config_addr = pe->config_addr; |
418 | if (pe->addr) | |
419 | config_addr = pe->addr; | |
eb594a47 GS |
420 | |
421 | if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) { | |
422 | ret = rtas_call(ibm_read_slot_reset_state2, 3, 4, rets, | |
371a395d GS |
423 | config_addr, BUID_HI(pe->phb->buid), |
424 | BUID_LO(pe->phb->buid)); | |
eb594a47 GS |
425 | } else if (ibm_read_slot_reset_state != RTAS_UNKNOWN_SERVICE) { |
426 | /* Fake PE unavailable info */ | |
427 | rets[2] = 0; | |
428 | ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, | |
371a395d GS |
429 | config_addr, BUID_HI(pe->phb->buid), |
430 | BUID_LO(pe->phb->buid)); | |
eb594a47 GS |
431 | } else { |
432 | return EEH_STATE_NOT_SUPPORT; | |
433 | } | |
434 | ||
435 | if (ret) | |
436 | return ret; | |
437 | ||
438 | /* Parse the result out */ | |
00ba05a1 GS |
439 | if (!rets[1]) |
440 | return EEH_STATE_NOT_SUPPORT; | |
441 | ||
442 | switch(rets[0]) { | |
443 | case 0: | |
444 | result = EEH_STATE_MMIO_ACTIVE | | |
445 | EEH_STATE_DMA_ACTIVE; | |
446 | break; | |
447 | case 1: | |
448 | result = EEH_STATE_RESET_ACTIVE | | |
449 | EEH_STATE_MMIO_ACTIVE | | |
450 | EEH_STATE_DMA_ACTIVE; | |
451 | break; | |
452 | case 2: | |
453 | result = 0; | |
454 | break; | |
455 | case 4: | |
456 | result = EEH_STATE_MMIO_ENABLED; | |
457 | break; | |
458 | case 5: | |
459 | if (rets[2]) { | |
460 | if (state) *state = rets[2]; | |
461 | result = EEH_STATE_UNAVAILABLE; | |
462 | } else { | |
eb594a47 GS |
463 | result = EEH_STATE_NOT_SUPPORT; |
464 | } | |
00ba05a1 GS |
465 | break; |
466 | default: | |
eb594a47 GS |
467 | result = EEH_STATE_NOT_SUPPORT; |
468 | } | |
469 | ||
470 | return result; | |
aa1e6374 GS |
471 | } |
472 | ||
473 | /** | |
474 | * pseries_eeh_reset - Reset the specified PE | |
371a395d | 475 | * @pe: EEH PE |
aa1e6374 GS |
476 | * @option: reset option |
477 | * | |
478 | * Reset the specified PE | |
479 | */ | |
371a395d | 480 | static int pseries_eeh_reset(struct eeh_pe *pe, int option) |
aa1e6374 | 481 | { |
2652481f GS |
482 | int config_addr; |
483 | int ret; | |
484 | ||
485 | /* Figure out PE address */ | |
371a395d GS |
486 | config_addr = pe->config_addr; |
487 | if (pe->addr) | |
488 | config_addr = pe->addr; | |
2652481f GS |
489 | |
490 | /* Reset PE through RTAS call */ | |
491 | ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, | |
371a395d GS |
492 | config_addr, BUID_HI(pe->phb->buid), |
493 | BUID_LO(pe->phb->buid), option); | |
2652481f GS |
494 | |
495 | /* If fundamental-reset not supported, try hot-reset */ | |
496 | if (option == EEH_RESET_FUNDAMENTAL && | |
497 | ret == -8) { | |
26833a50 | 498 | option = EEH_RESET_HOT; |
2652481f | 499 | ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, |
371a395d | 500 | config_addr, BUID_HI(pe->phb->buid), |
26833a50 | 501 | BUID_LO(pe->phb->buid), option); |
2652481f GS |
502 | } |
503 | ||
26833a50 GS |
504 | /* We need reset hold or settlement delay */ |
505 | if (option == EEH_RESET_FUNDAMENTAL || | |
506 | option == EEH_RESET_HOT) | |
507 | msleep(EEH_PE_RST_HOLD_TIME); | |
508 | else | |
509 | msleep(EEH_PE_RST_SETTLE_TIME); | |
510 | ||
2652481f | 511 | return ret; |
aa1e6374 GS |
512 | } |
513 | ||
514 | /** | |
515 | * pseries_eeh_wait_state - Wait for PE state | |
371a395d | 516 | * @pe: EEH PE |
2ac3990c | 517 | * @max_wait: maximal period in millisecond |
aa1e6374 GS |
518 | * |
519 | * Wait for the state of associated PE. It might take some time | |
520 | * to retrieve the PE's state. | |
521 | */ | |
371a395d | 522 | static int pseries_eeh_wait_state(struct eeh_pe *pe, int max_wait) |
aa1e6374 | 523 | { |
b0e5f742 GS |
524 | int ret; |
525 | int mwait; | |
526 | ||
527 | /* | |
528 | * According to PAPR, the state of PE might be temporarily | |
529 | * unavailable. Under the circumstance, we have to wait | |
530 | * for indicated time determined by firmware. The maximal | |
531 | * wait time is 5 minutes, which is acquired from the original | |
532 | * EEH implementation. Also, the original implementation | |
533 | * also defined the minimal wait time as 1 second. | |
534 | */ | |
535 | #define EEH_STATE_MIN_WAIT_TIME (1000) | |
536 | #define EEH_STATE_MAX_WAIT_TIME (300 * 1000) | |
537 | ||
538 | while (1) { | |
371a395d | 539 | ret = pseries_eeh_get_state(pe, &mwait); |
b0e5f742 GS |
540 | |
541 | /* | |
542 | * If the PE's state is temporarily unavailable, | |
543 | * we have to wait for the specified time. Otherwise, | |
544 | * the PE's state will be returned immediately. | |
545 | */ | |
546 | if (ret != EEH_STATE_UNAVAILABLE) | |
547 | return ret; | |
548 | ||
549 | if (max_wait <= 0) { | |
0dae2743 | 550 | pr_warn("%s: Timeout when getting PE's state (%d)\n", |
b0e5f742 GS |
551 | __func__, max_wait); |
552 | return EEH_STATE_NOT_SUPPORT; | |
553 | } | |
554 | ||
555 | if (mwait <= 0) { | |
0dae2743 | 556 | pr_warn("%s: Firmware returned bad wait value %d\n", |
b0e5f742 GS |
557 | __func__, mwait); |
558 | mwait = EEH_STATE_MIN_WAIT_TIME; | |
559 | } else if (mwait > EEH_STATE_MAX_WAIT_TIME) { | |
0dae2743 | 560 | pr_warn("%s: Firmware returned too long wait value %d\n", |
b0e5f742 GS |
561 | __func__, mwait); |
562 | mwait = EEH_STATE_MAX_WAIT_TIME; | |
563 | } | |
564 | ||
565 | max_wait -= mwait; | |
566 | msleep(mwait); | |
567 | } | |
568 | ||
569 | return EEH_STATE_NOT_SUPPORT; | |
aa1e6374 GS |
570 | } |
571 | ||
572 | /** | |
573 | * pseries_eeh_get_log - Retrieve error log | |
371a395d | 574 | * @pe: EEH PE |
aa1e6374 GS |
575 | * @severity: temporary or permanent error log |
576 | * @drv_log: driver log to be combined with retrieved error log | |
577 | * @len: length of driver log | |
578 | * | |
579 | * Retrieve the temporary or permanent error from the PE. | |
580 | * Actually, the error will be retrieved through the dedicated | |
581 | * RTAS call. | |
582 | */ | |
371a395d | 583 | static int pseries_eeh_get_log(struct eeh_pe *pe, int severity, char *drv_log, unsigned long len) |
aa1e6374 | 584 | { |
8d633291 GS |
585 | int config_addr; |
586 | unsigned long flags; | |
587 | int ret; | |
588 | ||
8d633291 GS |
589 | spin_lock_irqsave(&slot_errbuf_lock, flags); |
590 | memset(slot_errbuf, 0, eeh_error_buf_size); | |
591 | ||
592 | /* Figure out the PE address */ | |
371a395d GS |
593 | config_addr = pe->config_addr; |
594 | if (pe->addr) | |
595 | config_addr = pe->addr; | |
8d633291 GS |
596 | |
597 | ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr, | |
371a395d | 598 | BUID_HI(pe->phb->buid), BUID_LO(pe->phb->buid), |
8d633291 GS |
599 | virt_to_phys(drv_log), len, |
600 | virt_to_phys(slot_errbuf), eeh_error_buf_size, | |
601 | severity); | |
602 | if (!ret) | |
603 | log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); | |
604 | spin_unlock_irqrestore(&slot_errbuf_lock, flags); | |
605 | ||
606 | return ret; | |
aa1e6374 GS |
607 | } |
608 | ||
609 | /** | |
610 | * pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE | |
371a395d | 611 | * @pe: EEH PE |
aa1e6374 GS |
612 | * |
613 | * The function will be called to reconfigure the bridges included | |
614 | * in the specified PE so that the mulfunctional PE would be recovered | |
615 | * again. | |
616 | */ | |
371a395d | 617 | static int pseries_eeh_configure_bridge(struct eeh_pe *pe) |
aa1e6374 | 618 | { |
1823fbf1 GS |
619 | int config_addr; |
620 | int ret; | |
871e178e RC |
621 | /* Waiting 0.2s maximum before skipping configuration */ |
622 | int max_wait = 200; | |
1823fbf1 GS |
623 | |
624 | /* Figure out the PE address */ | |
371a395d GS |
625 | config_addr = pe->config_addr; |
626 | if (pe->addr) | |
627 | config_addr = pe->addr; | |
1823fbf1 | 628 | |
871e178e | 629 | while (max_wait > 0) { |
bd000b82 RC |
630 | ret = rtas_call(ibm_configure_pe, 3, 1, NULL, |
631 | config_addr, BUID_HI(pe->phb->buid), | |
632 | BUID_LO(pe->phb->buid)); | |
1823fbf1 | 633 | |
871e178e RC |
634 | if (!ret) |
635 | return ret; | |
636 | ||
637 | /* | |
638 | * If RTAS returns a delay value that's above 100ms, cut it | |
639 | * down to 100ms in case firmware made a mistake. For more | |
640 | * on how these delay values work see rtas_busy_delay_time | |
641 | */ | |
642 | if (ret > RTAS_EXTENDED_DELAY_MIN+2 && | |
643 | ret <= RTAS_EXTENDED_DELAY_MAX) | |
644 | ret = RTAS_EXTENDED_DELAY_MIN+2; | |
645 | ||
646 | max_wait -= rtas_busy_delay_time(ret); | |
647 | ||
648 | if (max_wait < 0) | |
649 | break; | |
650 | ||
651 | rtas_busy_delay(ret); | |
652 | } | |
1823fbf1 | 653 | |
1f52f176 | 654 | pr_warn("%s: Unable to configure bridge PHB#%x-PE#%x (%d)\n", |
871e178e | 655 | __func__, pe->phb->global_number, pe->addr, ret); |
1823fbf1 | 656 | return ret; |
aa1e6374 GS |
657 | } |
658 | ||
3780444c GS |
659 | /** |
660 | * pseries_eeh_read_config - Read PCI config space | |
0bd78587 | 661 | * @pdn: PCI device node |
3780444c GS |
662 | * @where: PCI address |
663 | * @size: size to read | |
664 | * @val: return value | |
665 | * | |
666 | * Read config space from the speicifed device | |
667 | */ | |
0bd78587 | 668 | static int pseries_eeh_read_config(struct pci_dn *pdn, int where, int size, u32 *val) |
3780444c | 669 | { |
3780444c GS |
670 | return rtas_read_config(pdn, where, size, val); |
671 | } | |
672 | ||
673 | /** | |
674 | * pseries_eeh_write_config - Write PCI config space | |
0bd78587 | 675 | * @pdn: PCI device node |
3780444c GS |
676 | * @where: PCI address |
677 | * @size: size to write | |
678 | * @val: value to be written | |
679 | * | |
680 | * Write config space to the specified device | |
681 | */ | |
0bd78587 | 682 | static int pseries_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 val) |
3780444c | 683 | { |
3780444c GS |
684 | return rtas_write_config(pdn, where, size, val); |
685 | } | |
686 | ||
aa1e6374 GS |
687 | static struct eeh_ops pseries_eeh_ops = { |
688 | .name = "pseries", | |
689 | .init = pseries_eeh_init, | |
ff57b454 | 690 | .probe = pseries_eeh_probe, |
aa1e6374 GS |
691 | .set_option = pseries_eeh_set_option, |
692 | .get_pe_addr = pseries_eeh_get_pe_addr, | |
693 | .get_state = pseries_eeh_get_state, | |
694 | .reset = pseries_eeh_reset, | |
695 | .wait_state = pseries_eeh_wait_state, | |
696 | .get_log = pseries_eeh_get_log, | |
3780444c | 697 | .configure_bridge = pseries_eeh_configure_bridge, |
131c123a | 698 | .err_inject = NULL, |
3780444c | 699 | .read_config = pseries_eeh_read_config, |
1d350544 GS |
700 | .write_config = pseries_eeh_write_config, |
701 | .next_error = NULL, | |
702 | .restore_config = NULL | |
aa1e6374 GS |
703 | }; |
704 | ||
705 | /** | |
706 | * eeh_pseries_init - Register platform dependent EEH operations | |
707 | * | |
708 | * EEH initialization on pseries platform. This function should be | |
709 | * called before any EEH related functions. | |
710 | */ | |
35e5cfe2 | 711 | static int __init eeh_pseries_init(void) |
aa1e6374 | 712 | { |
8e83e905 | 713 | int ret; |
3ea1ae98 GS |
714 | |
715 | ret = eeh_ops_register(&pseries_eeh_ops); | |
716 | if (!ret) | |
717 | pr_info("EEH: pSeries platform initialized\n"); | |
718 | else | |
719 | pr_info("EEH: pSeries platform initialization failure (%d)\n", | |
720 | ret); | |
721 | ||
722 | return ret; | |
aa1e6374 | 723 | } |
8e83e905 | 724 | machine_early_initcall(pseries, eeh_pseries_init); |