mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-03 07:38:10 +00:00
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
4 changed files with 168 additions and 1 deletions
|
@ -75,7 +75,7 @@ _GLOBAL(__setup_cpu_e500v2)
|
||||||
bl __e500_icache_setup
|
bl __e500_icache_setup
|
||||||
bl __e500_dcache_setup
|
bl __e500_dcache_setup
|
||||||
bl __setup_e500_ivors
|
bl __setup_e500_ivors
|
||||||
#ifdef CONFIG_FSL_RIO
|
#if defined(CONFIG_FSL_RIO) || defined(CONFIG_FSL_PCI)
|
||||||
/* Ensure that RFXE is set */
|
/* Ensure that RFXE is set */
|
||||||
mfspr r3,SPRN_HID1
|
mfspr r3,SPRN_HID1
|
||||||
oris r3,r3,HID1_RFXE@h
|
oris r3,r3,HID1_RFXE@h
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include <asm/switch_to.h>
|
#include <asm/switch_to.h>
|
||||||
#include <asm/tm.h>
|
#include <asm/tm.h>
|
||||||
#include <asm/debug.h>
|
#include <asm/debug.h>
|
||||||
|
#include <sysdev/fsl_pci.h>
|
||||||
|
|
||||||
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
|
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
|
||||||
int (*__debugger)(struct pt_regs *regs) __read_mostly;
|
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 (reason & MCSR_BUS_RBERR) {
|
||||||
if (fsl_rio_mcheck_exception(regs))
|
if (fsl_rio_mcheck_exception(regs))
|
||||||
return 1;
|
return 1;
|
||||||
|
if (fsl_pci_mcheck_exception(regs))
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk("Machine check in kernel mode.\n");
|
printk("Machine check in kernel mode.\n");
|
||||||
|
|
|
@ -26,11 +26,15 @@
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
#include <asm/pci-bridge.h>
|
#include <asm/pci-bridge.h>
|
||||||
|
#include <asm/ppc-pci.h>
|
||||||
#include <asm/machdep.h>
|
#include <asm/machdep.h>
|
||||||
|
#include <asm/disassemble.h>
|
||||||
|
#include <asm/ppc-opcode.h>
|
||||||
#include <sysdev/fsl_soc.h>
|
#include <sysdev/fsl_soc.h>
|
||||||
#include <sysdev/fsl_pci.h>
|
#include <sysdev/fsl_pci.h>
|
||||||
|
|
||||||
|
@ -868,6 +872,160 @@ u64 fsl_pci_immrbar_base(struct pci_controller *hose)
|
||||||
return 0;
|
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)
|
#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
|
||||||
static const struct of_device_id pci_ids[] = {
|
static const struct of_device_id pci_ids[] = {
|
||||||
{ .compatible = "fsl,mpc8540-pci", },
|
{ .compatible = "fsl,mpc8540-pci", },
|
||||||
|
|
|
@ -126,5 +126,11 @@ static inline int mpc85xx_pci_err_probe(struct platform_device *op)
|
||||||
}
|
}
|
||||||
#endif
|
#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 /* __POWERPC_FSL_PCI_H */
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
Loading…
Reference in a new issue