diff options
author | Hongtao Jia <hongtao.jia@freescale.com> | 2013-04-28 01:20:08 -0400 |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2013-07-30 16:50:07 -0400 |
commit | 4e0e3435b50285eafe5898124ce02f7577f6803a (patch) | |
tree | 1cb33868e383e1e94e339edfbcdf5206387f2667 | |
parent | 9123c5ed45a583311d8373e99f5e3ee4c0b4306b (diff) |
powerpc/85xx: Add machine check handler to fix PCIe erratum on mpc85xx
A PCIe erratum of mpc85xx may causes a core hang when a link of PCIe
goes down. when the link goes down, Non-posted transactions issued
via the ATMU requiring completion result in an instruction stall.
At the same time a machine-check exception is generated to the core
to allow further processing by the handler. We implements the handler
which skips the instruction caused the stall.
This patch depends on patch:
powerpc/85xx: Add platform_device declaration to fsl_pci.h
Signed-off-by: Zhao Chenhui <b35336@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: Liu Shuo <soniccat.liu@gmail.com>
Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
-rw-r--r-- | arch/powerpc/kernel/cpu_setup_fsl_booke.S | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/traps.c | 3 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_pci.c | 158 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_pci.h | 6 |
4 files changed, 168 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/cpu_setup_fsl_booke.S b/arch/powerpc/kernel/cpu_setup_fsl_booke.S index 0b9af015bedc..bfb18c7290b7 100644 --- a/arch/powerpc/kernel/cpu_setup_fsl_booke.S +++ b/arch/powerpc/kernel/cpu_setup_fsl_booke.S | |||
@@ -75,7 +75,7 @@ _GLOBAL(__setup_cpu_e500v2) | |||
75 | bl __e500_icache_setup | 75 | bl __e500_icache_setup |
76 | bl __e500_dcache_setup | 76 | bl __e500_dcache_setup |
77 | bl __setup_e500_ivors | 77 | bl __setup_e500_ivors |
78 | #ifdef CONFIG_FSL_RIO | 78 | #if defined(CONFIG_FSL_RIO) || defined(CONFIG_FSL_PCI) |
79 | /* Ensure that RFXE is set */ | 79 | /* Ensure that RFXE is set */ |
80 | mfspr r3,SPRN_HID1 | 80 | mfspr r3,SPRN_HID1 |
81 | oris r3,r3,HID1_RFXE@h | 81 | oris r3,r3,HID1_RFXE@h |
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index bf33c22e38a4..f58eaf23e8f1 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -62,6 +62,7 @@ | |||
62 | #include <asm/switch_to.h> | 62 | #include <asm/switch_to.h> |
63 | #include <asm/tm.h> | 63 | #include <asm/tm.h> |
64 | #include <asm/debug.h> | 64 | #include <asm/debug.h> |
65 | #include <sysdev/fsl_pci.h> | ||
65 | 66 | ||
66 | #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) | 67 | #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) |
67 | int (*__debugger)(struct pt_regs *regs) __read_mostly; | 68 | int (*__debugger)(struct pt_regs *regs) __read_mostly; |
@@ -567,6 +568,8 @@ int machine_check_e500(struct pt_regs *regs) | |||
567 | if (reason & MCSR_BUS_RBERR) { | 568 | if (reason & MCSR_BUS_RBERR) { |
568 | if (fsl_rio_mcheck_exception(regs)) | 569 | if (fsl_rio_mcheck_exception(regs)) |
569 | return 1; | 570 | return 1; |
571 | if (fsl_pci_mcheck_exception(regs)) | ||
572 | return 1; | ||
570 | } | 573 | } |
571 | 574 | ||
572 | printk("Machine check in kernel mode.\n"); | 575 | printk("Machine check in kernel mode.\n"); |
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 46ac1ddea683..3409758c78b2 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c | |||
@@ -26,11 +26,15 @@ | |||
26 | #include <linux/memblock.h> | 26 | #include <linux/memblock.h> |
27 | #include <linux/log2.h> | 27 | #include <linux/log2.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/uaccess.h> | ||
29 | 30 | ||
30 | #include <asm/io.h> | 31 | #include <asm/io.h> |
31 | #include <asm/prom.h> | 32 | #include <asm/prom.h> |
32 | #include <asm/pci-bridge.h> | 33 | #include <asm/pci-bridge.h> |
34 | #include <asm/ppc-pci.h> | ||
33 | #include <asm/machdep.h> | 35 | #include <asm/machdep.h> |
36 | #include <asm/disassemble.h> | ||
37 | #include <asm/ppc-opcode.h> | ||
34 | #include <sysdev/fsl_soc.h> | 38 | #include <sysdev/fsl_soc.h> |
35 | #include <sysdev/fsl_pci.h> | 39 | #include <sysdev/fsl_pci.h> |
36 | 40 | ||
@@ -868,6 +872,160 @@ u64 fsl_pci_immrbar_base(struct pci_controller *hose) | |||
868 | return 0; | 872 | return 0; |
869 | } | 873 | } |
870 | 874 | ||
875 | #ifdef CONFIG_E500 | ||
876 | static int mcheck_handle_load(struct pt_regs *regs, u32 inst) | ||
877 | { | ||
878 | unsigned int rd, ra, rb, d; | ||
879 | |||
880 | rd = get_rt(inst); | ||
881 | ra = get_ra(inst); | ||
882 | rb = get_rb(inst); | ||
883 | d = get_d(inst); | ||
884 | |||
885 | switch (get_op(inst)) { | ||
886 | case 31: | ||
887 | switch (get_xop(inst)) { | ||
888 | case OP_31_XOP_LWZX: | ||
889 | case OP_31_XOP_LWBRX: | ||
890 | regs->gpr[rd] = 0xffffffff; | ||
891 | break; | ||
892 | |||
893 | case OP_31_XOP_LWZUX: | ||
894 | regs->gpr[rd] = 0xffffffff; | ||
895 | regs->gpr[ra] += regs->gpr[rb]; | ||
896 | break; | ||
897 | |||
898 | case OP_31_XOP_LBZX: | ||
899 | regs->gpr[rd] = 0xff; | ||
900 | break; | ||
901 | |||
902 | case OP_31_XOP_LBZUX: | ||
903 | regs->gpr[rd] = 0xff; | ||
904 | regs->gpr[ra] += regs->gpr[rb]; | ||
905 | break; | ||
906 | |||
907 | case OP_31_XOP_LHZX: | ||
908 | case OP_31_XOP_LHBRX: | ||
909 | regs->gpr[rd] = 0xffff; | ||
910 | break; | ||
911 | |||
912 | case OP_31_XOP_LHZUX: | ||
913 | regs->gpr[rd] = 0xffff; | ||
914 | regs->gpr[ra] += regs->gpr[rb]; | ||
915 | break; | ||
916 | |||
917 | case OP_31_XOP_LHAX: | ||
918 | regs->gpr[rd] = ~0UL; | ||
919 | break; | ||
920 | |||
921 | case OP_31_XOP_LHAUX: | ||
922 | regs->gpr[rd] = ~0UL; | ||
923 | regs->gpr[ra] += regs->gpr[rb]; | ||
924 | break; | ||
925 | |||
926 | default: | ||
927 | return 0; | ||
928 | } | ||
929 | break; | ||
930 | |||
931 | case OP_LWZ: | ||
932 | regs->gpr[rd] = 0xffffffff; | ||
933 | break; | ||
934 | |||
935 | case OP_LWZU: | ||
936 | regs->gpr[rd] = 0xffffffff; | ||
937 | regs->gpr[ra] += (s16)d; | ||
938 | break; | ||
939 | |||
940 | case OP_LBZ: | ||
941 | regs->gpr[rd] = 0xff; | ||
942 | break; | ||
943 | |||
944 | case OP_LBZU: | ||
945 | regs->gpr[rd] = 0xff; | ||
946 | regs->gpr[ra] += (s16)d; | ||
947 | break; | ||
948 | |||
949 | case OP_LHZ: | ||
950 | regs->gpr[rd] = 0xffff; | ||
951 | break; | ||
952 | |||
953 | case OP_LHZU: | ||
954 | regs->gpr[rd] = 0xffff; | ||
955 | regs->gpr[ra] += (s16)d; | ||
956 | break; | ||
957 | |||
958 | case OP_LHA: | ||
959 | regs->gpr[rd] = ~0UL; | ||
960 | break; | ||
961 | |||
962 | case OP_LHAU: | ||
963 | regs->gpr[rd] = ~0UL; | ||
964 | regs->gpr[ra] += (s16)d; | ||
965 | break; | ||
966 | |||
967 | default: | ||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | return 1; | ||
972 | } | ||
973 | |||
974 | static int is_in_pci_mem_space(phys_addr_t addr) | ||
975 | { | ||
976 | struct pci_controller *hose; | ||
977 | struct resource *res; | ||
978 | int i; | ||
979 | |||
980 | list_for_each_entry(hose, &hose_list, list_node) { | ||
981 | if (!(hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG)) | ||
982 | continue; | ||
983 | |||
984 | for (i = 0; i < 3; i++) { | ||
985 | res = &hose->mem_resources[i]; | ||
986 | if ((res->flags & IORESOURCE_MEM) && | ||
987 | addr >= res->start && addr <= res->end) | ||
988 | return 1; | ||
989 | } | ||
990 | } | ||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | int fsl_pci_mcheck_exception(struct pt_regs *regs) | ||
995 | { | ||
996 | u32 inst; | ||
997 | int ret; | ||
998 | phys_addr_t addr = 0; | ||
999 | |||
1000 | /* Let KVM/QEMU deal with the exception */ | ||
1001 | if (regs->msr & MSR_GS) | ||
1002 | return 0; | ||
1003 | |||
1004 | #ifdef CONFIG_PHYS_64BIT | ||
1005 | addr = mfspr(SPRN_MCARU); | ||
1006 | addr <<= 32; | ||
1007 | #endif | ||
1008 | addr += mfspr(SPRN_MCAR); | ||
1009 | |||
1010 | if (is_in_pci_mem_space(addr)) { | ||
1011 | if (user_mode(regs)) { | ||
1012 | pagefault_disable(); | ||
1013 | ret = get_user(regs->nip, &inst); | ||
1014 | pagefault_enable(); | ||
1015 | } else { | ||
1016 | ret = probe_kernel_address(regs->nip, inst); | ||
1017 | } | ||
1018 | |||
1019 | if (mcheck_handle_load(regs, inst)) { | ||
1020 | regs->nip += 4; | ||
1021 | return 1; | ||
1022 | } | ||
1023 | } | ||
1024 | |||
1025 | return 0; | ||
1026 | } | ||
1027 | #endif | ||
1028 | |||
871 | #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) | 1029 | #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) |
872 | static const struct of_device_id pci_ids[] = { | 1030 | static const struct of_device_id pci_ids[] = { |
873 | { .compatible = "fsl,mpc8540-pci", }, | 1031 | { .compatible = "fsl,mpc8540-pci", }, |
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h index 72b5625330e2..defc422a375f 100644 --- a/arch/powerpc/sysdev/fsl_pci.h +++ b/arch/powerpc/sysdev/fsl_pci.h | |||
@@ -126,5 +126,11 @@ static inline int mpc85xx_pci_err_probe(struct platform_device *op) | |||
126 | } | 126 | } |
127 | #endif | 127 | #endif |
128 | 128 | ||
129 | #ifdef CONFIG_FSL_PCI | ||
130 | extern int fsl_pci_mcheck_exception(struct pt_regs *); | ||
131 | #else | ||
132 | static inline int fsl_pci_mcheck_exception(struct pt_regs *regs) {return 0; } | ||
133 | #endif | ||
134 | |||
129 | #endif /* __POWERPC_FSL_PCI_H */ | 135 | #endif /* __POWERPC_FSL_PCI_H */ |
130 | #endif /* __KERNEL__ */ | 136 | #endif /* __KERNEL__ */ |