[PPC440SPe] Convert machine check exceptions handling

Convert using fixup mechanism to suppressing MCK for the duration of config
read/write transaction: while fixups work fine with the case of a precise
exception, we identified a major drawback with this approach when there's
an imprecise case. In this scenario there is the following race condition:
the fixup is (by design) set to catch the instruction following the one
actually causing the exception; if an interrupt (e.g. decrementer) happens
between those two instructions, the ISR code is executed before the fixup
handler the machine check is no longer protected by the fixup handler as it
appears as within the ISR code. In consequence the fixup approach is being
phased out and replaced with explicit suppressing of MCK during a PCIe
config read/write cycle.

Signed-off-by: Grzegorz Bernacki <gjb@semihalf.com>
diff --git a/cpu/ppc4xx/440spe_pcie.c b/cpu/ppc4xx/440spe_pcie.c
index bf68cc1..2d0b406 100644
--- a/cpu/ppc4xx/440spe_pcie.c
+++ b/cpu/ppc4xx/440spe_pcie.c
@@ -40,31 +40,24 @@
 	LNKW_X8			= 0x8
 };
 
-static inline int pcie_in_8(const volatile unsigned char __iomem *addr)
+static void pcie_dmer_disable(void)
 {
-	int ret;
-
-	PCIE_IN(lbzx, ret, addr);
-
-	return ret;
+	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE),
+		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) | GPL_DMER_MASK_DISA);
+	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE),
+		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) | GPL_DMER_MASK_DISA);
+	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE),
+		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) | GPL_DMER_MASK_DISA);
 }
 
-static inline int pcie_in_le16(const volatile unsigned short __iomem *addr)
+static void pcie_dmer_enable(void)
 {
-	int ret;
-
-	PCIE_IN(lhbrx, ret, addr)
-
-	return ret;
-}
-
-static inline unsigned pcie_in_le32(const volatile unsigned __iomem *addr)
-{
-	unsigned ret;
-
-	PCIE_IN(lwbrx, ret, addr);
-
-	return ret;
+	mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE0_BASE),
+		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) & ~GPL_DMER_MASK_DISA);
+	mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE1_BASE),
+		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) & ~GPL_DMER_MASK_DISA);
+	mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE2_BASE),
+		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) & ~GPL_DMER_MASK_DISA);
 }
 
 
@@ -81,17 +74,27 @@
 	devfn = PCI_BDF(0,0,0);
 	offset += devfn << 4;
 
+	/*
+	 * Reading from configuration space of non-existing device can
+	 * generate transaction errors. For the read duration we suppress
+	 * assertion of machine check exceptions to avoid those.
+	 */
+	pcie_dmer_disable ();
+
 	switch (len) {
 	case 1:
-		*val = pcie_in_8(hose->cfg_data + offset);
+		*val = in_8(hose->cfg_data + offset);
 		break;
 	case 2:
-		*val = pcie_in_le16((u16 *)(hose->cfg_data + offset));
+		*val = in_le16((u16 *)(hose->cfg_data + offset));
 		break;
 	default:
-		*val = pcie_in_le32((u32*)(hose->cfg_data + offset));
+		*val = in_le32((u32*)(hose->cfg_data + offset));
 		break;
 	}
+
+	pcie_dmer_enable ();
+
 	return 0;
 }
 
@@ -107,6 +110,11 @@
 	devfn = PCI_BDF(0,0,0);
 	offset += devfn << 4;
 
+	/*
+	 * Suppress MCK exceptions, similar to pcie_read_config()
+	 */
+	pcie_dmer_disable ();
+
 	switch (len) {
 	case 1:
 		out_8(hose->cfg_data + offset, val);
@@ -118,6 +126,9 @@
 		out_le32((u32 *)(hose->cfg_data + offset), val);
 		break;
 	}
+
+	pcie_dmer_enable ();
+
 	return 0;
 }