Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e0e3435 authored by Hongtao Jia's avatar Hongtao Jia Committed by Scott Wood
Browse files

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: default avatarZhao Chenhui <b35336@freescale.com>
Signed-off-by: default avatarLi Yang <leoli@freescale.com>
Signed-off-by: default avatarLiu Shuo <soniccat.liu@gmail.com>
Signed-off-by: default avatarJia Hongtao <hongtao.jia@freescale.com>
Signed-off-by: default avatarScott Wood <scottwood@freescale.com>
parent 9123c5ed
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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");
+158 −0
Original line number Diff line number Diff line
@@ -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", },
+6 −0
Original line number Diff line number Diff line
@@ -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__ */