aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorGavin Shan <shangw@linux.vnet.ibm.com>2012-09-07 18:44:15 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-09-09 19:35:41 -0400
commit9e6d2cf65e3dbaf783917c92c15d31d419b0d648 (patch)
tree3333cebc80097095c1e07bf5935f85d0b5695d3c /arch/powerpc
parent371a395d2f1fe296c89735547672d70f4dcc2949 (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')
-rw-r--r--arch/powerpc/include/asm/eeh.h3
-rw-r--r--arch/powerpc/include/asm/ppc-pci.h1
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c79
-rw-r--r--arch/powerpc/platforms/pseries/eeh_pe.c94
4 files changed, 97 insertions, 80 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 5e45a1c5c9e8..629fb27c093e 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -171,6 +171,9 @@ typedef void *(*eeh_traverse_func)(void *data, void *flag);
171int __devinit eeh_phb_pe_create(struct pci_controller *phb); 171int __devinit eeh_phb_pe_create(struct pci_controller *phb);
172int eeh_add_to_parent_pe(struct eeh_dev *edev); 172int eeh_add_to_parent_pe(struct eeh_dev *edev);
173int eeh_rmv_from_parent_pe(struct eeh_dev *edev); 173int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
174void *eeh_pe_dev_traverse(struct eeh_pe *root,
175 eeh_traverse_func fn, void *flag);
176void eeh_pe_restore_bars(struct eeh_pe *pe);
174 177
175void * __devinit eeh_dev_init(struct device_node *dn, void *data); 178void * __devinit eeh_dev_init(struct device_node *dn, void *data);
176void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb); 179void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb);
diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h
index 3e301b14e09f..5cbe3f2a7e80 100644
--- a/arch/powerpc/include/asm/ppc-pci.h
+++ b/arch/powerpc/include/asm/ppc-pci.h
@@ -54,7 +54,6 @@ struct pci_dev *pci_addr_cache_get_device(unsigned long addr);
54void eeh_slot_error_detail(struct eeh_dev *edev, int severity); 54void eeh_slot_error_detail(struct eeh_dev *edev, int severity);
55int eeh_pci_enable(struct eeh_dev *edev, int function); 55int eeh_pci_enable(struct eeh_dev *edev, int function);
56int eeh_reset_pe(struct eeh_dev *); 56int eeh_reset_pe(struct eeh_dev *);
57void eeh_restore_bars(struct eeh_dev *);
58int rtas_write_config(struct pci_dn *, int where, int size, u32 val); 57int rtas_write_config(struct pci_dn *, int where, int size, u32 val);
59int rtas_read_config(struct pci_dn *, int where, int size, u32 *val); 58int rtas_read_config(struct pci_dn *, int where, int size, u32 *val);
60void eeh_pe_state_mark(struct eeh_pe *pe, int state); 59void eeh_pe_state_mark(struct eeh_pe *pe, int state);
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 */
629static 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 */
679void 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 */
183void *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 */
512static 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 */
560void eeh_pe_restore_bars(struct eeh_pe *pe)
561{
562 eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
563}