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>
This commit is contained in:
parent
9123c5ed45
commit
4e0e3435b5
|
@ -75,7 +75,7 @@ _GLOBAL(__setup_cpu_e500v2)
|
|||
bl __e500_icache_setup
|
||||
bl __e500_dcache_setup
|
||||
bl __setup_e500_ivors
|
||||
#ifdef CONFIG_FSL_RIO
|
||||
#if defined(CONFIG_FSL_RIO) || defined(CONFIG_FSL_PCI)
|
||||
/* Ensure that RFXE is set */
|
||||
mfspr r3,SPRN_HID1
|
||||
oris r3,r3,HID1_RFXE@h
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include <asm/switch_to.h>
|
||||
#include <asm/tm.h>
|
||||
#include <asm/debug.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
||||
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
|
||||
int (*__debugger)(struct pt_regs *regs) __read_mostly;
|
||||
|
@ -567,6 +568,8 @@ int machine_check_e500(struct pt_regs *regs)
|
|||
if (reason & MCSR_BUS_RBERR) {
|
||||
if (fsl_rio_mcheck_exception(regs))
|
||||
return 1;
|
||||
if (fsl_pci_mcheck_exception(regs))
|
||||
return 1;
|
||||
}
|
||||
|
||||
printk("Machine check in kernel mode.\n");
|
||||
|
|
|
@ -26,11 +26,15 @@
|
|||
#include <linux/memblock.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/ppc-pci.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/disassemble.h>
|
||||
#include <asm/ppc-opcode.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
||||
|
@ -868,6 +872,160 @@ u64 fsl_pci_immrbar_base(struct pci_controller *hose)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_E500
|
||||
static int mcheck_handle_load(struct pt_regs *regs, u32 inst)
|
||||
{
|
||||
unsigned int rd, ra, rb, d;
|
||||
|
||||
rd = get_rt(inst);
|
||||
ra = get_ra(inst);
|
||||
rb = get_rb(inst);
|
||||
d = get_d(inst);
|
||||
|
||||
switch (get_op(inst)) {
|
||||
case 31:
|
||||
switch (get_xop(inst)) {
|
||||
case OP_31_XOP_LWZX:
|
||||
case OP_31_XOP_LWBRX:
|
||||
regs->gpr[rd] = 0xffffffff;
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LWZUX:
|
||||
regs->gpr[rd] = 0xffffffff;
|
||||
regs->gpr[ra] += regs->gpr[rb];
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LBZX:
|
||||
regs->gpr[rd] = 0xff;
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LBZUX:
|
||||
regs->gpr[rd] = 0xff;
|
||||
regs->gpr[ra] += regs->gpr[rb];
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LHZX:
|
||||
case OP_31_XOP_LHBRX:
|
||||
regs->gpr[rd] = 0xffff;
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LHZUX:
|
||||
regs->gpr[rd] = 0xffff;
|
||||
regs->gpr[ra] += regs->gpr[rb];
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LHAX:
|
||||
regs->gpr[rd] = ~0UL;
|
||||
break;
|
||||
|
||||
case OP_31_XOP_LHAUX:
|
||||
regs->gpr[rd] = ~0UL;
|
||||
regs->gpr[ra] += regs->gpr[rb];
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_LWZ:
|
||||
regs->gpr[rd] = 0xffffffff;
|
||||
break;
|
||||
|
||||
case OP_LWZU:
|
||||
regs->gpr[rd] = 0xffffffff;
|
||||
regs->gpr[ra] += (s16)d;
|
||||
break;
|
||||
|
||||
case OP_LBZ:
|
||||
regs->gpr[rd] = 0xff;
|
||||
break;
|
||||
|
||||
case OP_LBZU:
|
||||
regs->gpr[rd] = 0xff;
|
||||
regs->gpr[ra] += (s16)d;
|
||||
break;
|
||||
|
||||
case OP_LHZ:
|
||||
regs->gpr[rd] = 0xffff;
|
||||
break;
|
||||
|
||||
case OP_LHZU:
|
||||
regs->gpr[rd] = 0xffff;
|
||||
regs->gpr[ra] += (s16)d;
|
||||
break;
|
||||
|
||||
case OP_LHA:
|
||||
regs->gpr[rd] = ~0UL;
|
||||
break;
|
||||
|
||||
case OP_LHAU:
|
||||
regs->gpr[rd] = ~0UL;
|
||||
regs->gpr[ra] += (s16)d;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int is_in_pci_mem_space(phys_addr_t addr)
|
||||
{
|
||||
struct pci_controller *hose;
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(hose, &hose_list, list_node) {
|
||||
if (!(hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
res = &hose->mem_resources[i];
|
||||
if ((res->flags & IORESOURCE_MEM) &&
|
||||
addr >= res->start && addr <= res->end)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsl_pci_mcheck_exception(struct pt_regs *regs)
|
||||
{
|
||||
u32 inst;
|
||||
int ret;
|
||||
phys_addr_t addr = 0;
|
||||
|
||||
/* Let KVM/QEMU deal with the exception */
|
||||
if (regs->msr & MSR_GS)
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_PHYS_64BIT
|
||||
addr = mfspr(SPRN_MCARU);
|
||||
addr <<= 32;
|
||||
#endif
|
||||
addr += mfspr(SPRN_MCAR);
|
||||
|
||||
if (is_in_pci_mem_space(addr)) {
|
||||
if (user_mode(regs)) {
|
||||
pagefault_disable();
|
||||
ret = get_user(regs->nip, &inst);
|
||||
pagefault_enable();
|
||||
} else {
|
||||
ret = probe_kernel_address(regs->nip, inst);
|
||||
}
|
||||
|
||||
if (mcheck_handle_load(regs, inst)) {
|
||||
regs->nip += 4;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
|
||||
static const struct of_device_id pci_ids[] = {
|
||||
{ .compatible = "fsl,mpc8540-pci", },
|
||||
|
|
|
@ -126,5 +126,11 @@ static inline int mpc85xx_pci_err_probe(struct platform_device *op)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSL_PCI
|
||||
extern int fsl_pci_mcheck_exception(struct pt_regs *);
|
||||
#else
|
||||
static inline int fsl_pci_mcheck_exception(struct pt_regs *regs) {return 0; }
|
||||
#endif
|
||||
|
||||
#endif /* __POWERPC_FSL_PCI_H */
|
||||
#endif /* __KERNEL__ */
|
||||
|
|
Loading…
Reference in New Issue