diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2013-06-20 01:20:52 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-06-20 03:05:35 -0400 |
commit | 317f06de78152e0eb0aab5881d69e4c5cdf9f1fe (patch) | |
tree | 7e3253345db6e8bab2089eee8cd268ac0c90ac9b /arch/powerpc/kernel/eeh_pe.c | |
parent | a84f273c30500c0d5816e07232e3326a61956016 (diff) |
powerpc/eeh: Move common part to kernel directory
The patch moves the common part of EEH core into arch/powerpc/kernel
directory so that we needn't PPC_PSERIES while compiling POWERNV
platform:
* Move the EEH common part into arch/powerpc/kernel
* Move the functions for PCI hotplug from pSeries platform to
arch/powerpc/kernel/pci-hotplug.c
* Move CONFIG_EEH from arch/powerpc/platforms/pseries/Kconfig to
arch/powerpc/platforms/Kconfig
* Adjust makefile 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/kernel/eeh_pe.c')
-rw-r--r-- | arch/powerpc/kernel/eeh_pe.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c new file mode 100644 index 000000000000..9d4a9e8562b2 --- /dev/null +++ b/arch/powerpc/kernel/eeh_pe.c | |||
@@ -0,0 +1,653 @@ | |||
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 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 | list_for_each_entry(pe, &eeh_phb_pe, child) { | ||
103 | /* | ||
104 | * Actually, we needn't check the type since | ||
105 | * the PE for PHB has been determined when that | ||
106 | * was created. | ||
107 | */ | ||
108 | if ((pe->type & EEH_PE_PHB) && pe->phb == phb) | ||
109 | return pe; | ||
110 | } | ||
111 | |||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * eeh_pe_next - Retrieve the next PE in the tree | ||
117 | * @pe: current PE | ||
118 | * @root: root PE | ||
119 | * | ||
120 | * The function is used to retrieve the next PE in the | ||
121 | * hierarchy PE tree. | ||
122 | */ | ||
123 | static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, | ||
124 | struct eeh_pe *root) | ||
125 | { | ||
126 | struct list_head *next = pe->child_list.next; | ||
127 | |||
128 | if (next == &pe->child_list) { | ||
129 | while (1) { | ||
130 | if (pe == root) | ||
131 | return NULL; | ||
132 | next = pe->child.next; | ||
133 | if (next != &pe->parent->child_list) | ||
134 | break; | ||
135 | pe = pe->parent; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | return list_entry(next, struct eeh_pe, child); | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * eeh_pe_traverse - Traverse PEs in the specified PHB | ||
144 | * @root: root PE | ||
145 | * @fn: callback | ||
146 | * @flag: extra parameter to callback | ||
147 | * | ||
148 | * The function is used to traverse the specified PE and its | ||
149 | * child PEs. The traversing is to be terminated once the | ||
150 | * callback returns something other than NULL, or no more PEs | ||
151 | * to be traversed. | ||
152 | */ | ||
153 | static void *eeh_pe_traverse(struct eeh_pe *root, | ||
154 | eeh_traverse_func fn, void *flag) | ||
155 | { | ||
156 | struct eeh_pe *pe; | ||
157 | void *ret; | ||
158 | |||
159 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | ||
160 | ret = fn(pe, flag); | ||
161 | if (ret) return ret; | ||
162 | } | ||
163 | |||
164 | return NULL; | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * eeh_pe_dev_traverse - Traverse the devices from the PE | ||
169 | * @root: EEH PE | ||
170 | * @fn: function callback | ||
171 | * @flag: extra parameter to callback | ||
172 | * | ||
173 | * The function is used to traverse the devices of the specified | ||
174 | * PE and its child PEs. | ||
175 | */ | ||
176 | void *eeh_pe_dev_traverse(struct eeh_pe *root, | ||
177 | eeh_traverse_func fn, void *flag) | ||
178 | { | ||
179 | struct eeh_pe *pe; | ||
180 | struct eeh_dev *edev; | ||
181 | void *ret; | ||
182 | |||
183 | if (!root) { | ||
184 | pr_warning("%s: Invalid PE %p\n", __func__, root); | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | eeh_lock(); | ||
189 | |||
190 | /* Traverse root PE */ | ||
191 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | ||
192 | eeh_pe_for_each_dev(pe, edev) { | ||
193 | ret = fn(edev, flag); | ||
194 | if (ret) { | ||
195 | eeh_unlock(); | ||
196 | return ret; | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
201 | eeh_unlock(); | ||
202 | |||
203 | return NULL; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * __eeh_pe_get - Check the PE address | ||
208 | * @data: EEH PE | ||
209 | * @flag: EEH device | ||
210 | * | ||
211 | * For one particular PE, it can be identified by PE address | ||
212 | * or tranditional BDF address. BDF address is composed of | ||
213 | * Bus/Device/Function number. The extra data referred by flag | ||
214 | * indicates which type of address should be used. | ||
215 | */ | ||
216 | static void *__eeh_pe_get(void *data, void *flag) | ||
217 | { | ||
218 | struct eeh_pe *pe = (struct eeh_pe *)data; | ||
219 | struct eeh_dev *edev = (struct eeh_dev *)flag; | ||
220 | |||
221 | /* Unexpected PHB PE */ | ||
222 | if (pe->type & EEH_PE_PHB) | ||
223 | return NULL; | ||
224 | |||
225 | /* We prefer PE address */ | ||
226 | if (edev->pe_config_addr && | ||
227 | (edev->pe_config_addr == pe->addr)) | ||
228 | return pe; | ||
229 | |||
230 | /* Try BDF address */ | ||
231 | if (edev->pe_config_addr && | ||
232 | (edev->config_addr == pe->config_addr)) | ||
233 | return pe; | ||
234 | |||
235 | return NULL; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * eeh_pe_get - Search PE based on the given address | ||
240 | * @edev: EEH device | ||
241 | * | ||
242 | * Search the corresponding PE based on the specified address which | ||
243 | * is included in the eeh device. The function is used to check if | ||
244 | * the associated PE has been created against the PE address. It's | ||
245 | * notable that the PE address has 2 format: traditional PE address | ||
246 | * which is composed of PCI bus/device/function number, or unified | ||
247 | * PE address. | ||
248 | */ | ||
249 | static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev) | ||
250 | { | ||
251 | struct eeh_pe *root = eeh_phb_pe_get(edev->phb); | ||
252 | struct eeh_pe *pe; | ||
253 | |||
254 | pe = eeh_pe_traverse(root, __eeh_pe_get, edev); | ||
255 | |||
256 | return pe; | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * eeh_pe_get_parent - Retrieve the parent PE | ||
261 | * @edev: EEH device | ||
262 | * | ||
263 | * The whole PEs existing in the system are organized as hierarchy | ||
264 | * tree. The function is used to retrieve the parent PE according | ||
265 | * to the parent EEH device. | ||
266 | */ | ||
267 | static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) | ||
268 | { | ||
269 | struct device_node *dn; | ||
270 | struct eeh_dev *parent; | ||
271 | |||
272 | /* | ||
273 | * It might have the case for the indirect parent | ||
274 | * EEH device already having associated PE, but | ||
275 | * the direct parent EEH device doesn't have yet. | ||
276 | */ | ||
277 | dn = edev->dn->parent; | ||
278 | while (dn) { | ||
279 | /* We're poking out of PCI territory */ | ||
280 | if (!PCI_DN(dn)) return NULL; | ||
281 | |||
282 | parent = of_node_to_eeh_dev(dn); | ||
283 | /* We're poking out of PCI territory */ | ||
284 | if (!parent) return NULL; | ||
285 | |||
286 | if (parent->pe) | ||
287 | return parent->pe; | ||
288 | |||
289 | dn = dn->parent; | ||
290 | } | ||
291 | |||
292 | return NULL; | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * eeh_add_to_parent_pe - Add EEH device to parent PE | ||
297 | * @edev: EEH device | ||
298 | * | ||
299 | * Add EEH device to the parent PE. If the parent PE already | ||
300 | * exists, the PE type will be changed to EEH_PE_BUS. Otherwise, | ||
301 | * we have to create new PE to hold the EEH device and the new | ||
302 | * PE will be linked to its parent PE as well. | ||
303 | */ | ||
304 | int eeh_add_to_parent_pe(struct eeh_dev *edev) | ||
305 | { | ||
306 | struct eeh_pe *pe, *parent; | ||
307 | |||
308 | eeh_lock(); | ||
309 | |||
310 | /* | ||
311 | * Search the PE has been existing or not according | ||
312 | * to the PE address. If that has been existing, the | ||
313 | * PE should be composed of PCI bus and its subordinate | ||
314 | * components. | ||
315 | */ | ||
316 | pe = eeh_pe_get(edev); | ||
317 | if (pe && !(pe->type & EEH_PE_INVALID)) { | ||
318 | if (!edev->pe_config_addr) { | ||
319 | eeh_unlock(); | ||
320 | pr_err("%s: PE with addr 0x%x already exists\n", | ||
321 | __func__, edev->config_addr); | ||
322 | return -EEXIST; | ||
323 | } | ||
324 | |||
325 | /* Mark the PE as type of PCI bus */ | ||
326 | pe->type = EEH_PE_BUS; | ||
327 | edev->pe = pe; | ||
328 | |||
329 | /* Put the edev to PE */ | ||
330 | list_add_tail(&edev->list, &pe->edevs); | ||
331 | eeh_unlock(); | ||
332 | pr_debug("EEH: Add %s to Bus PE#%x\n", | ||
333 | edev->dn->full_name, pe->addr); | ||
334 | |||
335 | return 0; | ||
336 | } else if (pe && (pe->type & EEH_PE_INVALID)) { | ||
337 | list_add_tail(&edev->list, &pe->edevs); | ||
338 | edev->pe = pe; | ||
339 | /* | ||
340 | * We're running to here because of PCI hotplug caused by | ||
341 | * EEH recovery. We need clear EEH_PE_INVALID until the top. | ||
342 | */ | ||
343 | parent = pe; | ||
344 | while (parent) { | ||
345 | if (!(parent->type & EEH_PE_INVALID)) | ||
346 | break; | ||
347 | parent->type &= ~EEH_PE_INVALID; | ||
348 | parent = parent->parent; | ||
349 | } | ||
350 | eeh_unlock(); | ||
351 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", | ||
352 | edev->dn->full_name, pe->addr, pe->parent->addr); | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | /* Create a new EEH PE */ | ||
358 | pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE); | ||
359 | if (!pe) { | ||
360 | eeh_unlock(); | ||
361 | pr_err("%s: out of memory!\n", __func__); | ||
362 | return -ENOMEM; | ||
363 | } | ||
364 | pe->addr = edev->pe_config_addr; | ||
365 | pe->config_addr = edev->config_addr; | ||
366 | |||
367 | /* | ||
368 | * Put the new EEH PE into hierarchy tree. If the parent | ||
369 | * can't be found, the newly created PE will be attached | ||
370 | * to PHB directly. Otherwise, we have to associate the | ||
371 | * PE with its parent. | ||
372 | */ | ||
373 | parent = eeh_pe_get_parent(edev); | ||
374 | if (!parent) { | ||
375 | parent = eeh_phb_pe_get(edev->phb); | ||
376 | if (!parent) { | ||
377 | eeh_unlock(); | ||
378 | pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", | ||
379 | __func__, edev->phb->global_number); | ||
380 | edev->pe = NULL; | ||
381 | kfree(pe); | ||
382 | return -EEXIST; | ||
383 | } | ||
384 | } | ||
385 | pe->parent = parent; | ||
386 | |||
387 | /* | ||
388 | * Put the newly created PE into the child list and | ||
389 | * link the EEH device accordingly. | ||
390 | */ | ||
391 | list_add_tail(&pe->child, &parent->child_list); | ||
392 | list_add_tail(&edev->list, &pe->edevs); | ||
393 | edev->pe = pe; | ||
394 | eeh_unlock(); | ||
395 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", | ||
396 | edev->dn->full_name, pe->addr, pe->parent->addr); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | /** | ||
402 | * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE | ||
403 | * @edev: EEH device | ||
404 | * @purge_pe: remove PE or not | ||
405 | * | ||
406 | * The PE hierarchy tree might be changed when doing PCI hotplug. | ||
407 | * Also, the PCI devices or buses could be removed from the system | ||
408 | * during EEH recovery. So we have to call the function remove the | ||
409 | * corresponding PE accordingly if necessary. | ||
410 | */ | ||
411 | int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) | ||
412 | { | ||
413 | struct eeh_pe *pe, *parent, *child; | ||
414 | int cnt; | ||
415 | |||
416 | if (!edev->pe) { | ||
417 | pr_warning("%s: No PE found for EEH device %s\n", | ||
418 | __func__, edev->dn->full_name); | ||
419 | return -EEXIST; | ||
420 | } | ||
421 | |||
422 | eeh_lock(); | ||
423 | |||
424 | /* Remove the EEH device */ | ||
425 | pe = edev->pe; | ||
426 | edev->pe = NULL; | ||
427 | list_del(&edev->list); | ||
428 | |||
429 | /* | ||
430 | * Check if the parent PE includes any EEH devices. | ||
431 | * If not, we should delete that. Also, we should | ||
432 | * delete the parent PE if it doesn't have associated | ||
433 | * child PEs and EEH devices. | ||
434 | */ | ||
435 | while (1) { | ||
436 | parent = pe->parent; | ||
437 | if (pe->type & EEH_PE_PHB) | ||
438 | break; | ||
439 | |||
440 | if (purge_pe) { | ||
441 | if (list_empty(&pe->edevs) && | ||
442 | list_empty(&pe->child_list)) { | ||
443 | list_del(&pe->child); | ||
444 | kfree(pe); | ||
445 | } else { | ||
446 | break; | ||
447 | } | ||
448 | } else { | ||
449 | if (list_empty(&pe->edevs)) { | ||
450 | cnt = 0; | ||
451 | list_for_each_entry(child, &pe->child_list, child) { | ||
452 | if (!(child->type & EEH_PE_INVALID)) { | ||
453 | cnt++; | ||
454 | break; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | if (!cnt) | ||
459 | pe->type |= EEH_PE_INVALID; | ||
460 | else | ||
461 | break; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | pe = parent; | ||
466 | } | ||
467 | |||
468 | eeh_unlock(); | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /** | ||
474 | * __eeh_pe_state_mark - Mark the state for the PE | ||
475 | * @data: EEH PE | ||
476 | * @flag: state | ||
477 | * | ||
478 | * The function is used to mark the indicated state for the given | ||
479 | * PE. Also, the associated PCI devices will be put into IO frozen | ||
480 | * state as well. | ||
481 | */ | ||
482 | static void *__eeh_pe_state_mark(void *data, void *flag) | ||
483 | { | ||
484 | struct eeh_pe *pe = (struct eeh_pe *)data; | ||
485 | int state = *((int *)flag); | ||
486 | struct eeh_dev *tmp; | ||
487 | struct pci_dev *pdev; | ||
488 | |||
489 | /* | ||
490 | * Mark the PE with the indicated state. Also, | ||
491 | * the associated PCI device will be put into | ||
492 | * I/O frozen state to avoid I/O accesses from | ||
493 | * the PCI device driver. | ||
494 | */ | ||
495 | pe->state |= state; | ||
496 | eeh_pe_for_each_dev(pe, tmp) { | ||
497 | pdev = eeh_dev_to_pci_dev(tmp); | ||
498 | if (pdev) | ||
499 | pdev->error_state = pci_channel_io_frozen; | ||
500 | } | ||
501 | |||
502 | return NULL; | ||
503 | } | ||
504 | |||
505 | /** | ||
506 | * eeh_pe_state_mark - Mark specified state for PE and its associated device | ||
507 | * @pe: EEH PE | ||
508 | * | ||
509 | * EEH error affects the current PE and its child PEs. The function | ||
510 | * is used to mark appropriate state for the affected PEs and the | ||
511 | * associated devices. | ||
512 | */ | ||
513 | void eeh_pe_state_mark(struct eeh_pe *pe, int state) | ||
514 | { | ||
515 | eeh_lock(); | ||
516 | eeh_pe_traverse(pe, __eeh_pe_state_mark, &state); | ||
517 | eeh_unlock(); | ||
518 | } | ||
519 | |||
520 | /** | ||
521 | * __eeh_pe_state_clear - Clear state for the PE | ||
522 | * @data: EEH PE | ||
523 | * @flag: state | ||
524 | * | ||
525 | * The function is used to clear the indicated state from the | ||
526 | * given PE. Besides, we also clear the check count of the PE | ||
527 | * as well. | ||
528 | */ | ||
529 | static void *__eeh_pe_state_clear(void *data, void *flag) | ||
530 | { | ||
531 | struct eeh_pe *pe = (struct eeh_pe *)data; | ||
532 | int state = *((int *)flag); | ||
533 | |||
534 | pe->state &= ~state; | ||
535 | pe->check_count = 0; | ||
536 | |||
537 | return NULL; | ||
538 | } | ||
539 | |||
540 | /** | ||
541 | * eeh_pe_state_clear - Clear state for the PE and its children | ||
542 | * @pe: PE | ||
543 | * @state: state to be cleared | ||
544 | * | ||
545 | * When the PE and its children has been recovered from error, | ||
546 | * we need clear the error state for that. The function is used | ||
547 | * for the purpose. | ||
548 | */ | ||
549 | void eeh_pe_state_clear(struct eeh_pe *pe, int state) | ||
550 | { | ||
551 | eeh_lock(); | ||
552 | eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); | ||
553 | eeh_unlock(); | ||
554 | } | ||
555 | |||
556 | /** | ||
557 | * eeh_restore_one_device_bars - Restore the Base Address Registers for one device | ||
558 | * @data: EEH device | ||
559 | * @flag: Unused | ||
560 | * | ||
561 | * Loads the PCI configuration space base address registers, | ||
562 | * the expansion ROM base address, the latency timer, and etc. | ||
563 | * from the saved values in the device node. | ||
564 | */ | ||
565 | static void *eeh_restore_one_device_bars(void *data, void *flag) | ||
566 | { | ||
567 | int i; | ||
568 | u32 cmd; | ||
569 | struct eeh_dev *edev = (struct eeh_dev *)data; | ||
570 | struct device_node *dn = eeh_dev_to_of_node(edev); | ||
571 | |||
572 | for (i = 4; i < 10; i++) | ||
573 | eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); | ||
574 | /* 12 == Expansion ROM Address */ | ||
575 | eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); | ||
576 | |||
577 | #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) | ||
578 | #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) | ||
579 | |||
580 | eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, | ||
581 | SAVED_BYTE(PCI_CACHE_LINE_SIZE)); | ||
582 | eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, | ||
583 | SAVED_BYTE(PCI_LATENCY_TIMER)); | ||
584 | |||
585 | /* max latency, min grant, interrupt pin and line */ | ||
586 | eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); | ||
587 | |||
588 | /* | ||
589 | * Restore PERR & SERR bits, some devices require it, | ||
590 | * don't touch the other command bits | ||
591 | */ | ||
592 | eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); | ||
593 | if (edev->config_space[1] & PCI_COMMAND_PARITY) | ||
594 | cmd |= PCI_COMMAND_PARITY; | ||
595 | else | ||
596 | cmd &= ~PCI_COMMAND_PARITY; | ||
597 | if (edev->config_space[1] & PCI_COMMAND_SERR) | ||
598 | cmd |= PCI_COMMAND_SERR; | ||
599 | else | ||
600 | cmd &= ~PCI_COMMAND_SERR; | ||
601 | eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); | ||
602 | |||
603 | return NULL; | ||
604 | } | ||
605 | |||
606 | /** | ||
607 | * eeh_pe_restore_bars - Restore the PCI config space info | ||
608 | * @pe: EEH PE | ||
609 | * | ||
610 | * This routine performs a recursive walk to the children | ||
611 | * of this device as well. | ||
612 | */ | ||
613 | void eeh_pe_restore_bars(struct eeh_pe *pe) | ||
614 | { | ||
615 | /* | ||
616 | * We needn't take the EEH lock since eeh_pe_dev_traverse() | ||
617 | * will take that. | ||
618 | */ | ||
619 | eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL); | ||
620 | } | ||
621 | |||
622 | /** | ||
623 | * eeh_pe_bus_get - Retrieve PCI bus according to the given PE | ||
624 | * @pe: EEH PE | ||
625 | * | ||
626 | * Retrieve the PCI bus according to the given PE. Basically, | ||
627 | * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the | ||
628 | * primary PCI bus will be retrieved. The parent bus will be | ||
629 | * returned for BUS PE. However, we don't have associated PCI | ||
630 | * bus for DEVICE PE. | ||
631 | */ | ||
632 | struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe) | ||
633 | { | ||
634 | struct pci_bus *bus = NULL; | ||
635 | struct eeh_dev *edev; | ||
636 | struct pci_dev *pdev; | ||
637 | |||
638 | eeh_lock(); | ||
639 | |||
640 | if (pe->type & EEH_PE_PHB) { | ||
641 | bus = pe->phb->bus; | ||
642 | } else if (pe->type & EEH_PE_BUS || | ||
643 | pe->type & EEH_PE_DEVICE) { | ||
644 | edev = list_first_entry(&pe->edevs, struct eeh_dev, list); | ||
645 | pdev = eeh_dev_to_pci_dev(edev); | ||
646 | if (pdev) | ||
647 | bus = pdev->bus; | ||
648 | } | ||
649 | |||
650 | eeh_unlock(); | ||
651 | |||
652 | return bus; | ||
653 | } | ||