[PPC440SPe] Convert machine check exceptions handling
authorGrzegorz Bernacki <gjb@semihalf.com>
Fri, 7 Sep 2007 15:46:18 +0000 (17:46 +0200)
committerRafal Jaworowski <raj@semihalf.com>
Fri, 7 Sep 2007 15:46:18 +0000 (17:46 +0200)
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>
cpu/ppc4xx/440spe_pcie.c
cpu/ppc4xx/440spe_pcie.h
cpu/ppc4xx/traps.c

index bf68cc1e969a2a2b44419e16aa643af0d905850b..2d0b4067af71ae27c92675c5c2d2337af2c1c8f6 100644 (file)
@@ -40,31 +40,24 @@ enum {
        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;
-}
-
-static inline int pcie_in_le16(const volatile unsigned short __iomem *addr)
-{
-       int ret;
-
-       PCIE_IN(lhbrx, 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 unsigned pcie_in_le32(const volatile unsigned __iomem *addr)
+static void pcie_dmer_enable(void)
 {
-       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 @@ static int pcie_read_config(struct pci_controller *hose, unsigned int devfn,
        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 @@ static int pcie_write_config(struct pci_controller *hose, unsigned int devfn,
        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 @@ static int pcie_write_config(struct pci_controller *hose, unsigned int devfn,
                out_le32((u32 *)(hose->cfg_data + offset), val);
                break;
        }
+
+       pcie_dmer_enable ();
+
        return 0;
 }
 
index eb7cecf82fe49ed6b8b05573eb500cb3fa542273..38745eb797cdafa230c93cedd32db04bd12fa21a 100644 (file)
@@ -38,6 +38,7 @@
 #define DCRN_PEGPL_REGBAL(base)                (base + 0x13)
 #define DCRN_PEGPL_REGMSK(base)                (base + 0x14)
 #define DCRN_PEGPL_SPECIAL(base)       (base + 0x15)
+#define DCRN_PEGPL_CFG(base)           (base + 0x16)
 
 /*
  * System DCRs (SDRs)
        mtdcr(DCRN_SDR0_CFGADDR, offset); \
        mtdcr(DCRN_SDR0_CFGDATA,data);})
 
-#define PCIE_IN(opcode, ret, addr) \
-       __asm__ __volatile__(                   \
-               "sync\n"                        \
-               #opcode " %0,0,%1\n"            \
-               "1: twi 0,%0,0\n"               \
-               "isync\n"                       \
-               "b 3f\n"                        \
-               "2: li %0,-1\n"                 \
-               "3:\n"                          \
-               ".section __ex_table,\"a\"\n"   \
-               ".balign 4\n"                   \
-               ".long 1b,2b\n"                 \
-               ".previous\n"                   \
-               : "=r" (ret) : "r" (addr), "m" (*addr));
+#define GPL_DMER_MASK_DISA     0x02000000
 
 int ppc440spe_init_pcie(void);
 int ppc440spe_init_pcie_rootport(int port);
index f5365cb76a66937c1e3f7ac808d8aae890132d5f..38b6f89555007a9ac4614a4a88af3599af63d69c 100644 (file)
@@ -151,12 +151,6 @@ MachineCheckException(struct pt_regs *regs)
        int uncorr_ecc = 0;
 #endif
 
-       /* Probing PCI(E) using config cycles may cause this exception
-        * when a device is not present. To gracefully recover in such
-        * scenarios config read/write routines need to be instrumented in
-        * order to return via fixup handler. For examples refer to
-        * pcie_in_8(), pcie_in_le16() and pcie_in_le32()
-        */
        if ((fixup = search_exception_table(regs->nip)) != 0) {
                regs->nip = fixup;
                val = mfspr(MCSR);