diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2012-09-07 18:44:15 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-09-09 19:35:41 -0400 |
commit | 9e6d2cf65e3dbaf783917c92c15d31d419b0d648 (patch) | |
tree | 3333cebc80097095c1e07bf5935f85d0b5695d3c /arch/powerpc/platforms/pseries | |
parent | 371a395d2f1fe296c89735547672d70f4dcc2949 (diff) |
powerpc/eeh: Device bars restore based on PE
The patch introduces the function to traverse the devices of the
specified PE and its child PEs. Also, the restore on device bars
is implemented based on the traverse function.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 79 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_pe.c | 94 |
2 files changed, 94 insertions, 79 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 3c8658ea13f2..b5fcecb06731 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -610,85 +610,6 @@ int eeh_reset_pe(struct eeh_dev *edev) | |||
610 | return -1; | 610 | return -1; |
611 | } | 611 | } |
612 | 612 | ||
613 | /** Save and restore of PCI BARs | ||
614 | * | ||
615 | * Although firmware will set up BARs during boot, it doesn't | ||
616 | * set up device BAR's after a device reset, although it will, | ||
617 | * if requested, set up bridge configuration. Thus, we need to | ||
618 | * configure the PCI devices ourselves. | ||
619 | */ | ||
620 | |||
621 | /** | ||
622 | * eeh_restore_one_device_bars - Restore the Base Address Registers for one device | ||
623 | * @edev: PCI device associated EEH device | ||
624 | * | ||
625 | * Loads the PCI configuration space base address registers, | ||
626 | * the expansion ROM base address, the latency timer, and etc. | ||
627 | * from the saved values in the device node. | ||
628 | */ | ||
629 | static inline void eeh_restore_one_device_bars(struct eeh_dev *edev) | ||
630 | { | ||
631 | int i; | ||
632 | u32 cmd; | ||
633 | struct device_node *dn = eeh_dev_to_of_node(edev); | ||
634 | |||
635 | if (!edev->phb) | ||
636 | return; | ||
637 | |||
638 | for (i=4; i<10; i++) { | ||
639 | eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); | ||
640 | } | ||
641 | |||
642 | /* 12 == Expansion ROM Address */ | ||
643 | eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); | ||
644 | |||
645 | #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) | ||
646 | #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) | ||
647 | |||
648 | eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, | ||
649 | SAVED_BYTE(PCI_CACHE_LINE_SIZE)); | ||
650 | |||
651 | eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, | ||
652 | SAVED_BYTE(PCI_LATENCY_TIMER)); | ||
653 | |||
654 | /* max latency, min grant, interrupt pin and line */ | ||
655 | eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); | ||
656 | |||
657 | /* Restore PERR & SERR bits, some devices require it, | ||
658 | * don't touch the other command bits | ||
659 | */ | ||
660 | eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); | ||
661 | if (edev->config_space[1] & PCI_COMMAND_PARITY) | ||
662 | cmd |= PCI_COMMAND_PARITY; | ||
663 | else | ||
664 | cmd &= ~PCI_COMMAND_PARITY; | ||
665 | if (edev->config_space[1] & PCI_COMMAND_SERR) | ||
666 | cmd |= PCI_COMMAND_SERR; | ||
667 | else | ||
668 | cmd &= ~PCI_COMMAND_SERR; | ||
669 | eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * eeh_restore_bars - Restore the PCI config space info | ||
674 | * @edev: EEH device | ||
675 | * | ||
676 | * This routine performs a recursive walk to the children | ||
677 | * of this device as well. | ||
678 | */ | ||
679 | void eeh_restore_bars(struct eeh_dev *edev) | ||
680 | { | ||
681 | struct device_node *dn; | ||
682 | if (!edev) | ||
683 | return; | ||
684 | |||
685 | if ((edev->mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(edev->class_code)) | ||
686 | eeh_restore_one_device_bars(edev); | ||
687 | |||
688 | for_each_child_of_node(eeh_dev_to_of_node(edev), dn) | ||
689 | eeh_restore_bars(of_node_to_eeh_dev(dn)); | ||
690 | } | ||
691 | |||
692 | /** | 613 | /** |
693 | * eeh_save_bars - Save device bars | 614 | * eeh_save_bars - Save device bars |
694 | * @edev: PCI device associated EEH device | 615 | * @edev: PCI device associated EEH device |
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c index 0dac1b69582d..2b7e85b804a4 100644 --- a/arch/powerpc/platforms/pseries/eeh_pe.c +++ b/arch/powerpc/platforms/pseries/eeh_pe.c | |||
@@ -172,6 +172,38 @@ static void *eeh_pe_traverse(struct eeh_pe *root, | |||
172 | } | 172 | } |
173 | 173 | ||
174 | /** | 174 | /** |
175 | * eeh_pe_dev_traverse - Traverse the devices from the PE | ||
176 | * @root: EEH PE | ||
177 | * @fn: function callback | ||
178 | * @flag: extra parameter to callback | ||
179 | * | ||
180 | * The function is used to traverse the devices of the specified | ||
181 | * PE and its child PEs. | ||
182 | */ | ||
183 | void *eeh_pe_dev_traverse(struct eeh_pe *root, | ||
184 | eeh_traverse_func fn, void *flag) | ||
185 | { | ||
186 | struct eeh_pe *pe; | ||
187 | struct eeh_dev *edev; | ||
188 | void *ret; | ||
189 | |||
190 | if (!root) { | ||
191 | pr_warning("%s: Invalid PE %p\n", __func__, root); | ||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | /* Traverse root PE */ | ||
196 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | ||
197 | eeh_pe_for_each_dev(pe, edev) { | ||
198 | ret = fn(edev, flag); | ||
199 | if (ret) return ret; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | return NULL; | ||
204 | } | ||
205 | |||
206 | /** | ||
175 | * __eeh_pe_get - Check the PE address | 207 | * __eeh_pe_get - Check the PE address |
176 | * @data: EEH PE | 208 | * @data: EEH PE |
177 | * @flag: EEH device | 209 | * @flag: EEH device |
@@ -467,3 +499,65 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state) | |||
467 | { | 499 | { |
468 | eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); | 500 | eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); |
469 | } | 501 | } |
502 | |||
503 | /** | ||
504 | * eeh_restore_one_device_bars - Restore the Base Address Registers for one device | ||
505 | * @data: EEH device | ||
506 | * @flag: Unused | ||
507 | * | ||
508 | * Loads the PCI configuration space base address registers, | ||
509 | * the expansion ROM base address, the latency timer, and etc. | ||
510 | * from the saved values in the device node. | ||
511 | */ | ||
512 | static void *eeh_restore_one_device_bars(void *data, void *flag) | ||
513 | { | ||
514 | int i; | ||
515 | u32 cmd; | ||
516 | struct eeh_dev *edev = (struct eeh_dev *)data; | ||
517 | struct device_node *dn = eeh_dev_to_of_node(edev); | ||
518 | |||
519 | for (i = 4; i < 10; i++) | ||
520 | eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); | ||
521 | /* 12 == Expansion ROM Address */ | ||
522 | eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); | ||
523 | |||
524 | #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) | ||
525 | #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) | ||
526 | |||
527 | eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, | ||
528 | SAVED_BYTE(PCI_CACHE_LINE_SIZE)); | ||
529 | eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, | ||
530 | SAVED_BYTE(PCI_LATENCY_TIMER)); | ||
531 | |||
532 | /* max latency, min grant, interrupt pin and line */ | ||
533 | eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); | ||
534 | |||
535 | /* | ||
536 | * Restore PERR & SERR bits, some devices require it, | ||
537 | * don't touch the other command bits | ||
538 | */ | ||
539 | eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); | ||
540 | if (edev->config_space[1] & PCI_COMMAND_PARITY) | ||
541 | cmd |= PCI_COMMAND_PARITY; | ||
542 | else | ||
543 | cmd &= ~PCI_COMMAND_PARITY; | ||
544 | if (edev->config_space[1] & PCI_COMMAND_SERR) | ||
545 | cmd |= PCI_COMMAND_SERR; | ||
546 | else | ||
547 | cmd &= ~PCI_COMMAND_SERR; | ||
548 | eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); | ||
549 | |||
550 | return NULL; | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * eeh_pe_restore_bars - Restore the PCI config space info | ||
555 | * @pe: EEH PE | ||
556 | * | ||
557 | * This routine performs a recursive walk to the children | ||
558 | * of this device as well. | ||
559 | */ | ||
560 | void eeh_pe_restore_bars(struct eeh_pe *pe) | ||
561 | { | ||
562 | eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL); | ||
563 | } | ||